diff --git a/snippets/usePersistedState.md b/snippets/usePersistedState.md new file mode 100644 index 000000000..77036369c --- /dev/null +++ b/snippets/usePersistedState.md @@ -0,0 +1,81 @@ +--- +title: usePersistedState +tags: hooks,state,effect,advanced +--- + +Returns a stateful value, persisted in `localStorage`, and a function to update it. + +- Use the `useState()` hook to initialize the `value` to `defaultValue`. +- Use the `useRef()` hook to create a ref that will hold the `name` of the value in `localStorage`. +- Use 3 instances of the `useEffect()` hook for initialization, `value` change and `name` change respectively. +- When the component is first mounted, use `Storage.getItem()` to update `value` if there's a stored value or `Storage.setItem()` to persist the current value. +- When `value` is updated, use `Storage.setItem()` to store the new value. +- When `name` is updated, use `Storage.setItem()` to create the new key, update the `nameRef` and use `Storage.removeItem()` to remove the previous key from `localStorage`. +- **NOTE:** The hook is meant for use with primitive values (i.e. not objects) and doesn't account for changes to `localStorage` due to other code. Both of these issues can be easily handled (e.g. JSON serialization and handling the `'storage'` event). + +```jsx +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]; +}; +``` + +```jsx +const MyComponent = ({ name }) => { + const [val, setVal] = usePersistedState(name, 10); + return ( + { + setVal(e.target.value); + }} + /> + ); +}; + +const MyApp = () => { + const [name, setName] = React.useState('my-value'); + return ( + <> + + { + setName(e.target.value); + }} + /> + + ); +}; + +ReactDOM.render(, document.getElementById('root')); +``` diff --git a/snippets/useUnload.md b/snippets/useUnload.md index 1667982eb..199014f57 100644 --- a/snippets/useUnload.md +++ b/snippets/useUnload.md @@ -3,9 +3,9 @@ title: useUnload tags: hooks,effect,event,intermediate --- -A hook that handles for the `beforeunload` window event. +Handles the `beforeunload` window event. -- Use the `useRef()` hook to create a ref for the callback function, `fn`. +- Use the `useRef()` hook to create a ref for the callback function, `fn`. - Use the `useEffect()` hook and `EventTarget.addEventListener()` to handle the `'beforeunload'` (when the user is about to close the window). - Use `EventTarget.removeEventListener()` to perform cleanup after the component is unmounted.