usePersistentState: A simple interface to persists state variables in localStorage

receter

Andreas Riedmüller

Posted on May 8, 2023

usePersistentState: A simple interface to persists state variables in localStorage

This is my take on a persistent state hook. The interface is similar to useState and I prefixed the local storage key with use-persistent-state- to prevent issues with duplicate keys.

Did you know that keys in local storage can be as big as you want? Of course the 5MB storage limit per domain still applies.

Usage

const [dismissed setDismissed] = usePersistentState('my-infobox', false); 
Enter fullscreen mode Exit fullscreen mode

Javascript

import { useState, useEffect } from 'react';

export function usePersistentState(key, initialState) {
  const prefixedKey = 'use-persistent-state-' + key
  // read key from local storage if not found use default value
  const [value, setValue] = useState(() => {
    const storedValue = localStorage.getItem(prefixedKey);
    if (storedValue === null) {
      if (typeof initialState === 'function') {
        return initialState();
      } else {
        return initialState;
      }
    } else {
      return JSON.parse(storedValue);
    }
  });
  // update local storage when value changes
  useEffect(() => {
    localStorage.setItem(prefixedKey, JSON.stringify(value));
  }, [value, prefixedKey]);
  return [value, setValue];
}
Enter fullscreen mode Exit fullscreen mode

Typescript

import { useState, useEffect } from 'react';

export function usePersistentState<Type>(key: string, initialState: Type | (() => Type)): [Type, React.Dispatch<React.SetStateAction<Type>>] {
  const prefixedKey = 'use-persistent-state-' + key
  // read key from local storage if not found use default value
  const [value, setValue] = useState<Type>(() => {
    const storedValue = localStorage.getItem(prefixedKey);
    if (storedValue === null) {
      if (typeof initialState === 'function') {
        return (initialState as () => Type)();
      } else {
        return initialState;
      }
    } else {
      return JSON.parse(storedValue);
    }
  });
  // update local storage when value changes
  useEffect(() => {
    localStorage.setItem(prefixedKey, JSON.stringify(value));
  }, [value, prefixedKey]);
  return [value, setValue];
}
Enter fullscreen mode Exit fullscreen mode

There is a discussion for the Type of initialState here on StackOverflow:

https://stackoverflow.com/questions/76201173/how-to-safely-call-a-function-in-typescript-that-was-passed-as-a-parameter-of-t

Libraries

Here are some libraries and resources I found on the internet regarding this topic:

use-persisted-state by Donavon West

  • popular
  • no typescript
  • seems abandoned

https://github.com/donavon/use-persisted-state#readme
https://www.npmjs.com/package/use-persisted-state

use-persistent-state by Krombik

  • very new and not a lot of users
  • typescript support
  • author also had SSR in mind
  • Did not try it yet

https://github.com/Krombik/use-persistent-state
https://www.npmjs.com/package/use-persistent-state

More

And there is also an implementation with some discussion here:

https://gist.github.com/gragland/2970ae543df237a07be1dbbf810f23fe

If you find more resources regarding this topic feel free to drop a comment below, thanks!

💖 💪 🙅 🚩
receter
Andreas Riedmüller

Posted on May 8, 2023

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related