Building a Custom Hook: useLocalStorage

Published on 24.5.2019

React’s custom hooks allow you to extract and reuse logic across components without changing the component hierarchy. As React developers, we’re all familiar with the built-in hooks like useStateuseEffect, and useContext. But sometimes, we need to create our own hooks to encapsulate logic that needs to be shared across multiple components.

In this post, we’ll learn how to create a custom hook called useLocalStorage that automatically syncs a component’s state with the browser’s local storage. This is particularly useful for persisting state between page refreshes.


What Are Custom Hooks?

Custom hooks are JavaScript functions that allow you to reuse stateful logic in React. Unlike regular JavaScript functions, custom hooks follow a specific naming convention: they start with the word “use” (e.g., useLocalStorageuseFormState). This lets React know that the function uses hooks and should be treated in a special way.

Custom hooks don’t return JSX—they typically return values or functions that encapsulate specific logic, and you can use them just like built-in hooks inside functional components.


Why Use Custom Hooks?

Custom hooks are perfect for scenarios where:

  • You need to reuse state logic across multiple components.
  • You want to encapsulate complex behavior, like interacting with external APIs or managing side effects.
  • You want to keep your components clean and concise by abstracting away repetitive logic.

One great example of when a custom hook is beneficial is when you want to persist a component’s state to localStorage, so that the data survives across page reloads. Instead of manually handling local storage in every component, we can create a custom hook to handle it for us.


Creating the useLocalStorage Hook

Let’s build a custom hook called useLocalStorage. This hook will behave similarly to useState, but with the added feature of saving and retrieving the value from localStorage.

Step 1: Define the Hook

First, we’ll define the useLocalStorage hook. The goal of this hook is to:

  1. Read a value from localStorage when the component mounts.
  2. Provide a way to update that value and automatically save it back to localStorage.

Here’s how we can do that:

import { useState } from 'react';

function useLocalStorage(key, initialValue) {
  // Get the stored value from localStorage or use the initial value
  const storedValue = localStorage.getItem(key);
  const initial = storedValue ? JSON.parse(storedValue) : initialValue;

  // Set up state
  const [value, setValue] = useState(initial);

  // Update localStorage whenever the value changes
  const setStoredValue = (newValue) => {
    // If the new value is a function, we pass the current value to it
    const valueToStore = newValue instanceof Function ? newValue(value) : newValue;
    
    // Save the new value to localStorage
    localStorage.setItem(key, JSON.stringify(valueToStore));

    // Update the state
    setValue(valueToStore);
  };

  return [value, setStoredValue];
}

export default useLocalStorage;

How It Works

  • Retrieving the Stored Value: We first check if there’s a value already stored in localStorage using the key passed to the hook. If the value exists, we parse it and use that as the initial value. If not, we use the provided initialValue.
  • Setting the Value: When the state changes, we update both the localStorage and the component state. If the new value is a function (like when using the previous state to compute the next state), we pass the current state to it.
  • Returning the State and Setter: The hook returns the current value and a setter function (setStoredValue) that updates both the state and localStorage.

Using the useLocalStorage Hook in a Component

Now that we’ve built the hook, let’s see it in action. Below is an example of how you can use the useLocalStorage hook in a React component to persist the state of a counter across page reloads.

import React from 'react';
import useLocalStorage from './useLocalStorage';

function Counter() {
  // Use the useLocalStorage hook to manage the counter state
  const [count, setCount] = useLocalStorage('count', 0);

  const increment = () => {
    setCount(count + 1);
  };

  return (
    <div>
      <h1>Counter: {count}</h1>
      <button onClick={increment}>Increment</button>
    </div>
  );
}

export default Counter;

In this example, the counter value will be saved to localStorage under the key "count". Every time the page is reloaded, the counter will be restored to its last value, providing a persistent experience for the user.


Testing the useLocalStorage Hook

To test this out:

  1. Start the app and click the increment button a few times.
  2. Open the browser’s developer tools, go to the Application tab, and check the Local Storage section. You should see the key "count" and the value reflecting the current count.
  3. Reload the page, and you’ll see that the count remains the same because the value is being read from localStorage on page load.

Conclusion

Custom hooks are a powerful feature in React that allow us to abstract away complex logic and reuse it across components. The useLocalStorage hook is a perfect example of how custom hooks can be used to manage state with persistence across page reloads.

By building this custom hook, we’ve shown how you can use hooks to work with browser storage in a clean, reusable way. If you ever need to persist any state in your React apps, custom hooks like useLocalStorage are a great tool to have in your toolkit.

Comments

No comments yet. Why don’t you start the discussion?

Leave a Reply

Your email address will not be published. Required fields are marked *