DEV Community

Cover image for Persistent State in React
Akhila Ariyachandra
Akhila Ariyachandra

Posted on • Originally published at akhilaariyachandra.com

Persistent State in React

This was originally posted on my blog on May 26th, 2020. Check it out for my posts on React and JavaScript.

Sometimes we need to preserve the state in a React app in cases where we close the browser or reload the page. In this guide I'll show you a simple custom hook to store the state in localstorage.

This guide will not work as is in frameworks like Next.js or Gatsby because the components aren't first rendered in the browser and localstorage can't be accessed. If you want to learn more about how rendering differs in the browser and the server check out this awesome post.

First create a function called useStickyState with the initial state (initialState) as an argument.

import React from "react";

const useStickyState = (initialState = null) => {};
Enter fullscreen mode Exit fullscreen mode

Then let's initialize the state and return it and the function to change it.

import React from "react";

const useStickyState = (initialState = null) => {
  const [state, useState] = React.useState(initialState);

  return [state, setState];
};
Enter fullscreen mode Exit fullscreen mode

Next we'll add an effect to store the state in localstorage when the state changes. Since we need an key to store a value in local storage we'll add one as an argument.

import React from "react";

const useStickyState = (key = "sticky", initialState = null) => {
  const [state, useState] = React.useState(initialState);

  React.useEffect(() => {
    localStorage.setItem(key, state);
  }, [state]);

  return [state, setState];
};
Enter fullscreen mode Exit fullscreen mode

In its current form the state will always initialize with initialState, but we need to load the state from localstorage if available. We can use lazy initialization to check localstorage and use it's value if present. If not, use initialState instead.

import React from "react";

const useStickyState = (key = "sticky", initialState = null) => {
  const [state, setState] = React.useState(() => {
    const storedState = localStorage.getItem(key);

    return storedState ?? initialState;
  });

  React.useEffect(() => {
    localStorage.setItem(key, state);
  }, [state]);

  return [state, setState];
};
Enter fullscreen mode Exit fullscreen mode

To finish up the hook, let's add and return a function to remove clear up the value in localstorage.

import React from "react";

const useStickyState = (key = "sticky", initialState = null) => {
  const [state, setState] = React.useState(() => {
    const storedState = localStorage.getItem(key);

    return storedState ?? initialState;
  });

  React.useEffect(() => {
    localStorage.setItem(key, state);
  }, [state]);

  const clearState = () => localStorage.removeItem(key);

  return [state, setState, clearState];
};
Enter fullscreen mode Exit fullscreen mode

Wrapping up

Below is an example on how to use the useStickyState hook to save the value in an <input>

import React from "react";

const useStickyState = (key = "sticky", initialState = null) => {
  const [state, setState] = React.useState(() => {
    const storedState = localStorage.getItem(key);

    return storedState ?? initialState;
  });

  React.useEffect(() => {
    localStorage.setItem(key, state);
  }, [state]);

  const clearState = () => localStorage.removeItem(key);

  return [state, setState, clearState];
};

export default function App() {
  const [value, setValue, clearValue] = useStickyState(
    "sticky",
    "Hello World!!!"
  );

  return (
    <div className="App">
      <h1>{`Value : ${value}`}</h1>

      <input
        type="text"
        value={value}
        onChange={(e) => setValue(e.target.value)}
      />

      <button onClick={() => clearValue()}>Clear</button>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

You can check out a working example here.

Top comments (7)

Collapse
 
crimsonmed profile image
Médéric Burlet • Edited

This is however not the best solution. Their are moments were localstorage is not accessible. This also opens security concerns as you are saving all that data user side and it can be modified for many purpose but also very easily accessible.

This does not also does not track changes and there is no history and caching as well.
I would definitely recommend using a solution made for that purpose for any bigger app as this will have many issued on the long term

I would recommend to go for overmindjs.org/ it is very simple to put into place and to use with websites, it supports react, vue, angular and even react native for instance.

Collapse
 
akhilaariyachandra profile image
Akhila Ariyachandra

This is far from the best solution, its just a really simple one.... 😅

Collapse
 
alirezavalizade profile image
alireza valizade

Cool, thanks for writing, But this is not a good way of persistent state, You'll get performance issues, because DOM will be blocked LS. Better is just keep the state in simple key-value object, then we can set it to storage before leaving the page, or in specific amount of time.

Collapse
 
jamesthomson profile image
James Thomson

Why would the DOM be blocked? Maybe the main thread could be if the operation was huge, but if this is the case, you shouldn't be storing massive amounts of data in localStorage.

Collapse
 
alirezavalizade profile image
alireza valizade

Yes, exactly my pointe was it's not for saving huge data, better to use do not that on each render or change or even better to use IndexedDB, Check this out it's an example of blocking the ui by localStorage,
codepen.io/SitePoint/pen/GzLPJV

Thread Thread
 
jamesthomson profile image
James Thomson • Edited

Well, if anything, that actually proves how capable writing to localStorage can be. The fact that it can write 100,000 entries in less than 500ms is pretty impressive. Nonetheless, each use case has its ideal solution. I wouldn’t rule out this method because of an over amplified scenario such as writing 100,000 entries to localStorage

Collapse
 
bernardbaker profile image
Bernard Baker

Interesting use of local storage. I'm persisting API access quota in a similar way.