Store Values in localStorage with the React useEffect Hook

Kent C. Dodds
InstructorKent C. Dodds

Share this video with your friends

Send Tweet
Published 4 years ago
Updated 10 months ago

When you refresh the page in JavaScript, it will clear values that you possibly want to be persisted if you don't store it somewhere as in localStorage.

Storing something in localStorage is a side effect and in React 16.6 you can utilize useEffect to deal with exactly this type of situation. In this lesson, we'll use useEffect to set a state variable in state whenever state is updated.

We'll also look at some optimizations available to us with useEffect so we don't have it running every time the component renders.

Instructor: [00:00] I can click on this counter. That increments the count. When I refresh the page though, it's going to go back to zero. I want it to preserve that state somewhere in localStorage or something so that I can refresh the page and it will restore my count to what it was before.

[00:14] Storing something in localStorage is a side effect. That's exactly what I'm going to use from React. I'll use useEffect. Every time this render function runs, I want to have this useEffect run so I can set the count in localStorage.

[00:28] We'll say, "window.localStorage.setItem count count." Now if I go in here to my dev tools, we'll go over to application and localStorage. We're going to see the count right there. If I increment it, every single time I click, that count is going to get updated.

[00:47] If I refresh now, it actually gets reset to zero. That's because when this counter runs, it's initializing our count to zero. Right here, it's that first argument to useState. When this effect runs, that count value is zero.

[01:00] We need to initialize our state to the count value that is in our localStorage. Let's go ahead and do that. We'll get our initial count from window.localStorage.getItem count. That value could be null or undefined. We'll go ahead and say, "Or zero."

[01:19] Items in localStorage are actually a string. Let's go ahead and and pass that to the number constructor. Now we can pass that initial count to our useState. If I save that, I'll increment it a couple times. We refresh. It stays at four.

[01:32] There are two optimizations I want to make here. The first is every single time that this render method runs, we're going to be reading into localStorage. That could be a performance problem. It's definitely not optimal.

[01:43] What we can do in our useState is we can actually pass it a function which it will only call on the first render. If I change this to a function that returns a read of localStorage count and that function will only be run when this counter is rendered for the first time.

[01:58] The other optimization that we should make here is around the useEffect hook. This useEffect callback is going to be called after every single time our component rerenders.

[02:07] That works fine in our little application here, but there are many reasons that a counter could rerender. We only really care to update localStorage when the count changes specifically.

[02:16] If this counter is rerendered for any other reason, we don't need to update the count in localStorage. Effectively, we only want this callback to be run when the count value changes. The useEffect hook allows us to do this by passing an array as a second argument.

[02:31] If we pass count here, React will only run our callback when the count value changes. We'll save that. Now we have the functionality that we're looking for.

[02:43] In review, what we had to do to make this work is we imported useEffect from React. Then we used that useEffect hook to create a callback function that we want to have run after every time our component renders to set the localStorage value of count to the current state of count for our counter.

[02:58] We made that dependent on the count changing so that this callback only runs when the count is changed. Then we also initialized our state with this initial count, which we read from localStorage. We made that a function so that this code only runs when the counter is rendered initially.

Mostafa Hesham
Mostafa Hesham
~ 4 years ago

is useEffect() covers life cycle methods like ComponentWillRecevieProps()or static getDerivedStateFromProps() and how use them without classes ?

Kent C. Dodds (Remix)
Kent C. Dodds (Remix)instructor
~ 4 years ago

Hi Mostafa,

Checkout the docs on useEffect :)

~ 4 years ago

So if a use a function as argument to useState it is called just on the fisrt render ?! that's interesting!

~ 3 years ago

Not only useEffect() has side effect, even useState() is not pure as it access / mutable value outside the function. I my opinion, Hooks doesn't encapsulate the side effects boundary clearly. What do you think?

~ 3 years ago

When we render more than one counter, then react maintains 2 separate counters in state, but localStorage has only one count reference. I doesn't matter as it is only an example.