Persisting state in React

Sometimes, you might want to persist the state of your React components across page reloads. You can achieve this by using the localStorage
or sessionStorage
APIs. But creating custom hooks for this use case might get a little tricky.
These hook don't account for changes to the storage due to other code. This issue can be addressed by listening to the 'storage'
event on Window
and updating the state accordingly.
useLocalStorage
hook
In order to persist a stateful value to localStorage
, you can create a custom hook called useLocalStorage
. This hook will return a stateful value and a function to update it.
Implementing the hook requires you to use the useState()
hook with a function to initialize its value lazily. You can then use a try...catch
block and Storage.getItem()
to try and get the value from Window.localStorage
. If no value is found, use Storage.setItem()
to store the defaultValue
and use it as the initial state. If an error occurs, use defaultValue
as the initial state. Finally, define a function that will update the state variable with the passed value and use Storage.setItem()
to store it.
const useLocalStorage = (keyName, defaultValue) => { const [storedValue, setStoredValue] = React.useState(() => { try { const value = window.localStorage.getItem(keyName); if (value) { return JSON.parse(value); } else { window.localStorage.setItem(keyName, JSON.stringify(defaultValue)); return defaultValue; } } catch (err) { return defaultValue; } }); const setValue = newValue => { try { window.localStorage.setItem(keyName, JSON.stringify(newValue)); } catch (err) {} setStoredValue(newValue); }; return [storedValue, setValue]; }; const MyApp = () => { const [name, setName] = useLocalStorage('name', 'John'); return <input value={name} onChange={e => setName(e.target.value)} />; }; ReactDOM.createRoot(document.getElementById('root')).render( <MyApp /> );
useSessionStorage
hook
The exact same logic can be applied to persist a stateful value to sessionStorage
. You can create a custom hook called useSessionStorage
that returns a stateful value and a function to update it. The only difference is that you'll be using Window.sessionStorage
instead of Window.localStorage
.
const useSessionStorage = (keyName, defaultValue) => { const [storedValue, setStoredValue] = React.useState(() => { try { const value = window.sessionStorage.getItem(keyName); if (value) { return JSON.parse(value); } else { window.sessionStorage.setItem(keyName, JSON.stringify(defaultValue)); return defaultValue; } } catch (err) { return defaultValue; } }); const setValue = newValue => { try { window.sessionStorage.setItem(keyName, JSON.stringify(newValue)); } catch (err) {} setStoredValue(newValue); }; return [storedValue, setValue]; }; const MyApp = () => { const [name, setName] = useSessionStorage('name', 'John'); return <input value={name} onChange={e => setName(e.target.value)} />; }; ReactDOM.createRoot(document.getElementById('root')).render( <MyApp /> );