usePersistentState: A simple interface to persists state variables in localStorage
Tags: javascript, typescript, react
Published: 2023-05-08 10:52:26

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](
https://stackoverflow.com/questions/8345979/is-there-a-limit-to-the-key-length-for-localstorage)? Of course the 5MB storage limit per domain still applies.

## Usage

```javascript
const [dismissed setDismissed] = usePersistentState('my-infobox', false); 
```

## Javascript

```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];
}
```

## Typescript

```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];
}
```

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!