From f444ea8a893eb995367f4bcdcbffe9bc7c33e712 Mon Sep 17 00:00:00 2001 From: Angelos Chalaris Date: Sun, 29 Nov 2020 14:16:36 +0200 Subject: [PATCH] Add usePersistedState --- snippets/usePersistedState.md | 81 +++++++++++++++++++++++++++++++++++ snippets/useUnload.md | 4 +- 2 files changed, 83 insertions(+), 2 deletions(-) create mode 100644 snippets/usePersistedState.md 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.