JoshWComeau

useStickyState

Filed under
Snippets
on
in
January 22nd, 2021.
Jan 2021.
function useStickyState(defaultValue, key) {
  const [value, setValue] = React.useState(() => {
    const stickyValue = window.localStorage.getItem(key);

    return stickyValue !== null
      ? JSON.parse(stickyValue)
      : defaultValue;
  });

  React.useEffect(() => {
    window.localStorage.setItem(key, JSON.stringify(value));
  }, [key, value]);

  return [value, setValue];
}

Link to this headingContext

The useState React hook is great for state that should be freshly initialized on every visit, but what about for state that should be persisted between sessions?

A good example of this is filters. If I set a filter to sort based on price instead of newest items, that value should "stick", so that if I come back to this site in a week, it remembers that I want to sort by price.

The useStickyState hook works just like useState, except it backs up to (and restores from) localStorage.

Note that I've written a full post about this, with a lot more context. If you're curious to dig deeper, check it out here:

Link to this headingUsage

It's used just like React.useState, except it takes two arguments: a default value, and a key:

const SomeComponent() {
  const [color, setColor] = useStickyState('blue', 'persisted-color');
}

The second argument, key, will be used as the localStorage key. It's important that each useStickyState instance uses a unique value.

Link to this headingWarnings

This hook is not SSR-safe—if you use SSR, or a framework that makes use of it (eg. Gatsby, Next.js), you'll get an error with this hook as-is.

This is not an easy problem to solve; when the app is rendered on the server, we don't have access to the user's local-storage!

The easiest solution would be to only use this hook in client-side components. The useHasMounted hook will come in handy here!

I share more about this challenge in my blog post, The Perils of Rehydration.

Last updated on

January 22nd, 2021

# of hits