Persisting your React state in 9 lines
Tags: react, frontend, javascript
Published: 2019-05-07 06:07:31

---
title: Persisting your React state in 9 lines
published: true
description: This quick little article will teach you a nice technique for storing UI state between visits
tags: react, frontend, javascript
cover_image: https://images.unsplash.com/photo-1554992315-c4fcf240882c?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1000&q=80
canonical_url: https://selbekk.io/blog/2020/05/persisting-your-react-state-in-9-lines/
---

I was playing around with a project from [Frontend Mentor](https://beta.frontendmentor.io) this weekend, and I was implementing this theme switcher with React hooks. It struck me that persisting which theme I had chosen between reloads would be a nice feature. So let's build a hook that provides just that!

This article will take you through the process of creating a reusable custom hook that persists our state to local storage.

## Getting started

We're going to create a custom hook named `usePersistedState` to store our state to local storage. Our function should accept a key to store the state under, as well as the default value (in case we haven't saved anything yet). It will return the same API as `useState` (a tuple of the state and an updater function). Here's our hook signature:

```js
function usePersistedState(key, defaultValue) {
  // Some magic
  return [state, setState];
}
```

Even though we store our state in local storage, we're keeping a local runtime copy in a regular `setState` call. This is so that we can trigger re-renders, as well as improve access time slightly (accessing local storage might not be that quick). Finally, if localStorage is not available for some reason, we still have a working hook (although it won't persist the setting).

```js
function usePersistedState(key, defaultValue) {
  const [state, setState] = React.useState(defaultValue);
  return [state, setState];
}
```

## Saving data in local storage

Next up, let's start reading from local storage! The `localStorage` API is built into your browser, and lets you access values by calling the `getItem` function with a string key.

```js
function usePersistedState(key, defaultValue) {
  const [state, setState] = React.useState(
    localStorage.getItem(key) || defaultValue
  );
  return [state, setState];
}
```

Here, we set the default value of our `useState` call to be whatever we've had stored in localStorage, or the `defaultValue` we passed in as an argument. Next, let's implement updating our local storage as well. We're going to use a `useEffect` hook for that:

```js
function usePersistedState(key, defaultValue) {
  const [state, setState] = React.useState(
    localStorage.getItem(key) || defaultValue
  );
  useEffect(() => {
    localStorage.setItem(key, state);
  }, [key, state]);
  return [state, setState];
}
```

Clever, right? Every time we update our state, we should update what's stored in our local storage. If the key changes, we'd want to store our current state under the new key as well.

## What about complex values?

Although the local storage API is great, it can only store string values. This is kind of a pain - but we can get around this limitation by serializing our JavaScript objects to JSON whenever we update our state (and back again). We do this with the [`JSON.parse`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse) and [`JSON.stringify`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify) functions.

```js
function usePersistedState(key, defaultValue) {
  const [state, setState] = React.useState(
    JSON.parse(localStorage.getItem(key)) || defaultValue
  );
  useEffect(() => {
    localStorage.setItem(key, JSON.stringify(state));
  }, [key, state]);
  return [state, setState];
}
```

Now we support complex data structures too!

## A last performance optimization

Our current implementation has one performance pitfall - we're reading from local storage on every render! To make matters worse - we're doing it just to get the initial value for our `useState` call! Luckily, there's a way around this sort of issue. **By passing in a function to `useState`, the default value will only be run once!**

Let's implement this:

```js
function usePersistedState(key, defaultValue) {
  const [state, setState] = React.useState(
    () => JSON.parse(localStorage.getItem(key)) || defaultValue
  );
  useEffect(() => {
    localStorage.setItem(key, JSON.stringify(state));
  }, [key, state]);
  return [state, setState];
}
```

## Summing up!

And that's it! We've implemented a pretty neat piece of reusable code in a few lines of code. This is perfect for local settings like themes, font-sizes or whatever else UI state you'd like to persist between visits.

Here's the project I mentioned initially, complete with this very hook to save the selected theme. Try it out!

{% codesandbox qm7k80y9 %}

What is **your** favorite reusable hook?