React usePersistedState hook
Returns a stateful value, persisted in localStorage, and a function to update it.
- Use the
useState()hook to initialize thevaluetodefaultValue. - Use the
useRef()hook to create a ref that will hold thenameof the value inWindow.localStorage. - Use 3 instances of the
useEffect()hook for initialization,valuechange andnamechange respectively. - When the component is first mounted, use
Storage.getItem()to updatevalueif there's a stored value orStorage.setItem()to persist the current value. - When
valueis updated, useStorage.setItem()to store the new value. - When
nameis updated, useStorage.setItem()to create the new key, update thenameRefand 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.localStoragedue 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'));