React - How to persist the local state between rendering without any library? (custom useState Hook)

The creation of a custom useState Hook in three steps.

About

This article shows you how to persist your local state (ie React state) between re-render without the use of any state library

Example

The basic counter

We will use the classic React counter.

If you refresh the page or unmount this component, you will see in this example that you have lost the counter value.

  • The counter component
function Counter() {
  // Declare a new state variable, which we'll call "count"
  const [count, setCount] = React.useState(0);

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}
  • Result:
    • Increment the counter
    • Demount, mount the counter
    • And see how the counter value is just gone

Steps 1 Persisting the value in memory

If you persist it in memory, the value will be deleted if the user reload the page but not when your component unmount and mount again

The trick is to use:

  1. useEffect to persist the value at the end of the rendering if it has changed
  2. a function to get this stored value as the initial value of the useState function.

The counter component with a persistent in-memory value.

let memoryCount = 0;

function Counter() {
  // Declare a new state variable, which we'll call "count"
  const [count, setCount] = React.useState(memoryCount);
  
  // useEffect is called at the end of the rendering and only if count has changed
  React.useEffect(() => {
        memoryCount = count
    }, [count]);
    
  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}
  • Result:
    • Increment the counter
    • Mount and unmount the counter
    • See how the counter is preserved
    • However, if the page is reloaded the value is reset.

Steps 2: Persisting the value in local storage

To persist the value after a page load, you need a browser storage. In this example, we use the localStorage store.

The counter component with a persistent in-browser value.

function Counter() {
  let counterKey = 'counter' 
  const [count, setCount] = React.useState(()=> {
      localStorage.getItem(counterKey)!== null ? +localStorage.getItem(counterKey): 0
  });
  
  // useEffect is called at the end of the rendering and only if count has changed
  React.useEffect(() => {
       localStorage.setItem(counterKey, count)      
    }, [count]);
    
  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}
  • Result:
    • Increment the counter
    • Mount and unmount the counter
    • See how the counter is preserved
    • Even if the page is reloaded

Step 3 - Create your useState custom hook

This is an example written in typescript on how you can extract the previous useState and useEffect hook to create your own Hook.

Example of an in-memory useState custom Hook:

let memoryState: { [key: string]: any } = {};

function useMemoryState<S>(initial: S, variableName: string) {

    function getInitialState(): S {
        if (memoryState[variableName] !== undefined) {
            return memoryState[variableName];
        }
        return initial;
    }

    const [state, setState] = useState<S>(getInitialState)
    useEffect(() => {
        memoryState[variableName] = state
    }, [state, variableName]);
    return [state, setState] as const;

}





Discover More
React - Local component state (known as State)

This page is the local state of a component. In React, this local state is known as state A change of value triggers a rendering State is useful for handling data that is local to the component...



Share this page:
Follow us:
Task Runner