Design Converter
Education
Last updated on Feb 3, 2025
Last updated on Feb 3, 2025
Software Development Executive - I
Need to keep data in your React app even after a page refresh?
Sometimes, you want values to stay the same across browser sessions. That's where useLocalStorage comes in! This handy approach lets you store and retrieve data easily, keeping it available even when the user leaves and returns.
In this blog, you'll learn how to create a custom React hook that works with local storage. You'll also see a simple example of how to use it in a project.
Let's make data persistence in React easy!
Developers often seek a way to persist data between page reloads. Creating a custom hook called useLocalStorage can solve that challenge. It leverages browser's local storage to keep a stored value even if the page refreshes or the user closes the tab. This blog will show how to create a file called useLocalStorage inside a hooks folder in your project folder, define the necessary logic, and then use it throughout various component structures.
useLocalStorage allows a react hook to handle data in local storage seamlessly. The hook can parse JSON data to ensure that the data you retrieve has the correct data type. It also handles potential error scenarios through a catch block. This approach is particularly valuable when you want a default value or an initial value that remains persistent. Keep reading for a thorough breakdown of the steps, code, and underlying function details.
local storage is a browser storage mechanism for retaining key-value pairs. Each key must be a string, and the stored data remains available even after closing the browser window. This makes local storage an excellent choice for scenarios that need persistent data.
When you define an argument as a key string and supply a default value, the browser's local storage ensures your stored value remains intact. This means you can access that value and modify it at any point in your component. The combination of parse and JSON operations allows you to handle data that might otherwise be undefined if the item does not exist in local storage.
A react project typically includes a src directory where you place various .js file artifacts. Inside this directory, many developers create a hooks folder. That folder can contain a new file named useLocalStorage.js. In that file, define three functions or fewer, depending on your approach. The central function will be our uselocalstorage hook. The rest might focus on error handling or other optional tasks.
To get started, create a new react application using tools like Create React App. After generating your scaffolding, open the project folder to begin coding. For instance, you can do:
1npx create-react-app my-localstorage-app 2cd my-localstorage-app
From there, open your code editor and navigate to the src/hooks folder. Then add a new file to define the custom hook called uselocalstorage. This file will hold the logic for retrieving and updating values in local storage.
A custom hook called useLocalStorage offers a cleaner way to share the same logic across different parts of your application. You can handle parse operations, catch error events, and ensure the correct default value is used. Each time you retrieve or update your stored value, the hook takes care of the repetitive code involved in local storage interactions.
A custom hook:
Provides a reusable function for a consistent approach.
Uses the react hook pattern with a state variable for your value.
Simplifies code in your component since you only call a single function.
This leads to a more efficient method update in your code base.
Below is an example of how to define the uselocalstorage hook in a new file. The goal is to handle two arguments: a key string and an initial value. We will parse any existing data in local storage, handle undefined cases, and provide a final stored value to your component.
Use the import statement to bring in useState hook from react. That hook is necessary for maintaining a piece of state in your custom hook.
1// src/hooks/useLocalStorage.js 2import { useState } from "react";
In many cases, you may want to import other react features, but the usestate hook is most critical for tracking the current value.
Below is the function signature. Notice that the function name is capitalized to indicate a react hook:
1function useLocalStorage(keyString, initialValue) { 2 // Implementation will go here 3}
Here, keyString is your key, and initialValue is the default or fallback value. The function will attempt to parse any existing data from browser's local storage. If no data is found, it will return the default value.
Inside the function, create logic that uses window.localStorage to find existing data. Then parse it with JSON.parse. A catch error step is usually added to guard against malformed data.
1function useLocalStorage(keyString, initialValue) { 2 const [storedValue, setStoredValue] = useState(() => { 3 try { 4 const existingValue = window.localStorage.getItem(keyString); 5 return existingValue ? JSON.parse(existingValue) : initialValue; 6 } catch (error) { 7 console.console(error); 8 return initialValue; 9 } 10 }); 11 12 // More code to come 13 14 return [storedValue, setStoredValue]; 15}
• We use a try block to handle parse.
• If JSON.parse fails, we catch error.
• The parse operation transforms the local storage string into the expected data type.
When the user wants to modify the stored value, the function for updating must also update browser's local storage. By using setStoredValue, we ensure the UI stays in sync. Then we employ JSON.stringify to store the new data.
1function useLocalStorage(keyString, initialValue) { 2 const [storedValue, setStoredValue] = useState(() => { 3 try { 4 const existingValue = window.localStorage.getItem(keyString); 5 return existingValue ? JSON.parse(existingValue) : initialValue; 6 } catch (error) { 7 console.console(error); 8 return initialValue; 9 } 10 }); 11 12 function setValue(newValue) { 13 try { 14 setStoredValue(newValue); 15 window.localStorage.setItem(keyString, JSON.stringify(newValue)); 16 } catch (error) { 17 console.console(error); 18 } 19 } 20 21 return [storedValue, setValue]; 22}
Notice the function setValue receives newValue as an argument. That newValue is then stored both in the state variable (storedValue) and in local storage.
Wrap up everything by exporting the hook function from your js file. This allows you to import it into any react component:
1import { useState } from "react"; 2 3function useLocalStorage(keyString, initialValue) { 4 const [storedValue, setStoredValue] = useState(() => { 5 try { 6 const existingValue = window.localStorage.getItem(keyString); 7 return existingValue ? JSON.parse(existingValue) : initialValue; 8 } catch (error) { 9 console.console(error); 10 return initialValue; 11 } 12 }); 13 14 function setValue(newValue) { 15 try { 16 setStoredValue(newValue); 17 window.localStorage.setItem(keyString, JSON.stringify(newValue)); 18 } catch (error) { 19 console.console(error); 20 } 21 } 22 23 return [storedValue, setValue]; 24} 25 26export default useLocalStorage;
We have now defined a custom hook that keeps your data synced with local storage. It handles parse, JSON operations, and error fallback. We have also included a catch to log any error to the console.
To demonstrate the hook in action, consider a small react component that stores a user’s name and retrieves it after reload. We will show the code in a straightforward form. A real production component might have more complexity, but the core idea remains the same.
1// src/App.js 2import React from "react"; 3import useLocalStorage from "./hooks/useLocalStorage"; 4 5function App() { 6 const [name, setName] = useLocalStorage("usernameKey", ""); 7 8 return ( 9 <div> 10 <h1>Welcome, {name}</h1> 11 <input 12 type="text" 13 placeholder="Enter your name" 14 value={name} 15 onChange={(e) => setName(e.target.value)} 16 /> 17 </div> 18 ); 19} 20 21export default App;
• We import the uselocalstorage hook from a js file.
• The first argument to useLocalStorage is keyString ("usernameKey").
• The second argument is an initial value, here an empty string.
• The stored value in local storage becomes the name.
• The new file with our custom hook can be used in any component across the code base.
The custom hook is essentially a function that interacts with two arguments, a key and an initial value. This function leverages the react hook mechanism to store and manage a piece of state. Each time you call setValue, you update both the local storage entry and the hook’s state. Below is a deeper breakdown:
Initialization
• The function attempts to parse existing local storage data.
• If local storage returns undefined or has an error, we fall back to the initial value.
Maintaining State
• The usestate hook is used to keep track of the current stored value.
• This stored value is in sync with browser storage.
Update Logic
• Any new value triggers the state variable update.
• The method update also includes the call to window.localStorage.setItem with JSON.stringify.
JSON Operations
• JSON parse helps retrieve data from local storage if it exists.
• JSON stringify ensures the object or string is saved as valid JSON.
Error Handling
• A catch block captures parse issues or other local storage failures.
• The error is printed to the console to highlight problems during development.
With these steps, the hook remains robust for storing numerous data structures.
When you define your custom hook, you may encounter situations where the default value is false or an empty string. Additional logic can be inserted to handle transformations, check for a certain data type, or manage multiple local storage keys at once. The system is flexible, as you can pass any initial or default value that suits your requirements.
In some cases, you may want to handle advanced parse logic or transform the data in a specific way before storing. For such scenarios, you can create extra function blocks or watch for specific error patterns in your catch error statements. By doing this, you can safely parse and store the data.
In a larger react application, you might have multiple components that need persistent data. By importing the custom hook called uselocalstorage in each relevant component, you can easily store and retrieve values. Each component can supply a different key string. The uselocalstorage custom hook ensures that each key-value pair in local storage remains distinct.
This approach simplifies state management across pages because you only write the local storage retrieval and parse code once. Then, each usage of the hook in a new component can focus on using the stored value without rewriting all the boilerplate.
Check the Browser
• Use the Application tab in your browser’s developer tools to see the stored keys and their JSON data.
• Confirm that the correct key string is present and that the stored item is not undefined.
Use console Logs
• Place console statements in your catch block to print the error if parse fails.
• Inspect your code to confirm that each function call is properly executed.
Validate Data Type
• Ensure that the parse operation returns the data type you expect.
• Convert strings to arrays or objects if needed once they have been retrieved from local storage.
Watch for JSON Parse Errors
• If your data is malformed, json parse may throw an error.
• The catch statement will help you pinpoint the cause quickly.
Below is a quick example that stores an array of tasks. We supply a default value of an empty array. The same logic can hold for any data structure.
1import React, { useState } from "react"; 2import useLocalStorage from "./hooks/useLocalStorage"; 3 4function TaskManager() { 5 const [tasks, setTasks] = useLocalStorage("taskList", []); 6 7 const [newTask, setNewTask] = useState(""); 8 9 function addTask() { 10 const updatedTasks = [...tasks, newTask]; 11 setTasks(updatedTasks); 12 setNewTask(""); 13 } 14 15 return ( 16 <div> 17 <h2>Task List</h2> 18 <input 19 type="text" 20 value={newTask} 21 onChange={(e) => setNewTask(e.target.value)} 22 /> 23 <button onClick={addTask}>Add Task</button> 24 <ul> 25 {tasks.map((t, i) => ( 26 <li key={i}>{t}</li> 27 ))} 28 </ul> 29 </div> 30 ); 31} 32 33export default TaskManager;
We define tasks in local storage with a key of taskList.
The default value is an empty array.
On each addition, a new value is appended, and the stored value updates.
In conclusion, using a custom useLocalStorage hook simplifies how you handle persistent data in React applications. By centralizing JSON parsing, error handling, and local storage interactions, this approach ensures consistency and maintainability across components. Whether you're saving simple preferences or more complex data, the pattern stays the same, making your app easier to manage. This method not only reduces code repetition but also improves user experiences by ensuring that data remains accessible across sessions.
Tired of manually designing screens, coding on weekends, and technical debt? Let DhiWise handle it for you!
You can build an e-commerce store, healthcare app, portfolio, blogging website, social media or admin panel right away. Use our library of 40+ pre-built free templates to create your first application using DhiWise.