React usePersistedState hook
Returns a stateful value, persisted in localStorage
, and a function to update it.
- Use the
useState()
hook to initialize thevalue
todefaultValue
. - Use the
useRef()
hook to create a ref that will hold thename
of the value inWindow.localStorage
. - Use 3 instances of the
useEffect()
hook for initialization,value
change andname
change respectively. - When the component is first mounted, use
Storage.getItem()
to updatevalue
if there's a stored value orStorage.setItem()
to persist the current value. - When
value
is updated, useStorage.setItem()
to store the new value. - When
name
is updated, useStorage.setItem()
to create the new key, update thenameRef
and useStorage.removeItem()
to remove the previous key fromWindow.localStorage
. - Note: The hook is meant for use with primitive values (i.e. not objects) and doesn't account for changes to
Window.localStorage
due to other code. Both of these issues can be easily handled (e.g. JSON serialization and handling the'storage'
event).
const usePersistedState = (name, defaultValue) => {
const [value, setValue] = React.useState(defaultValue);
const nameRef = React.useRef(name);
React.useEffect(() => {
try {
const storedValue = localStorage.getItem(name);
if (storedValue !== null) setValue(storedValue);
else localStorage.setItem(name, defaultValue);
} catch {
setValue(defaultValue);
}
}, []);
React.useEffect(() => {
try {
localStorage.setItem(nameRef.current, value);
} catch {}
}, [value]);
React.useEffect(() => {
const lastName = nameRef.current;
if (name !== lastName) {
try {
localStorage.setItem(name, value);
nameRef.current = name;
localStorage.removeItem(lastName);
} catch {}
}
}, [name]);
return [value, setValue];
};
const MyComponent = ({ name }) => {
const [val, setVal] = usePersistedState(name, 10);
return (
<input
value={val}
onChange={e => {
setVal(e.target.value);
}}
/>
);
};
const MyApp = () => {
const [name, setName] = React.useState('my-value');
return (
<>
<MyComponent name={name} />
<input
value={name}
onChange={e => {
setName(e.target.value);
}}
/>
</>
);
};
ReactDOM.render(<MyApp />, document.getElementById('root'));