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:
- useEffect to persist the value at the end of the rendering if it has changed
- 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;
}