skip to Main Content

I followed this tutorial to use localStorage in React:

import { useState, useEffect } from 'react';

function App() {
  // In the original code, this is inside the `useVolume` hook
  const [volumes, setVolumes] = useState([0, 0, 0]);

  useEffect(() => {
    const savedVolumes = localStorage.getItem('volumes');

    if (savedVolumes) {
      const parsedVolumes = JSON.parse(savedVolumes);
      setVolumes(parsedVolumes);
    }
  }, []);

  useEffect(() => {
    localStorage.setItem('volumes', JSON.stringify(volumes));
  }, [volumes]);

  // In the original code, this is inside the `useVolume` hook
  // with other functions that update `volumes`
  const updateVolumes = () => {
    const newVolumes = [0, 0, 1];
    setVolumes(newVolumes);
  };

  return (
    <div className="App">
      <div>{JSON.stringify(volumes)}</div>
      <button onClick={updateVolumes}>Update Volumes</button>
    </div>
  );
}

export default App;

When I press the Update Volumes button, the volumes localStorage changes to [0, 0, 1], but when I reload the page, volumes is back to [0, 0, 0] (localStorage is also back to [0, 0 ,0]).

How to change this code so that volumes and localStorage are [0, 0, 1] even after reloading the page?

Live code at StackBlitz

Note: I placed volumes, setVolumes, and updateVolumes in App() to simplify the example. In the original app, they are in a hook. That’s why I decoupled localStorage from their logic. I thought keeping it separate from the hook would be a better separation of concerns.

2

Answers


  1. The reason is because you use StrictMode

    On strict mode react run your components twice which cause to reset volume in localstorge.

    ReactDOM.createRoot(document.getElementById('root')!).render(
      <React.StrictMode>
        <App />
      </React.StrictMode>
    )
    

    Remove the strict mode and it will start to work as expected. However when you design your components make sure no matter how many times it get called the result is same.

    I would recommend you to read the documentation instead of those old tutorials. https://react.dev/reference/react/useEffect


    UPDATED

    You don’t need that 2nd useEffect. We exactly know when the volumes is about to update. So just save it in the eventhandler

      const updateVolumes = () => {
        const newVolumes = [0, 0, 1];
        setVolumes(newVolumes);
        localStorage.setItem('volumes', JSON.stringify(newVolumes));
      };
    

    Notice I used newVolumes not the volumes because setVolumes does not update the state but just schedule an update to do later. So the volumes is still holding the old state.

    Check this for more info – https://react.dev/learn/you-might-not-need-an-effect

    Login or Signup to reply.
  2. let’s walk through your code and you’ll see why thats happening. you are setting the state by default to [0, 0, 0] on mount both useEffect will run essentially overwriting whatever was in localstorage what you can do is something like this

    const savedVolumes = localStorage.getItem("volumes");
      const [volumes, setVolumes] = useState(JSON.parse(savedVolumes) ?? [0, 0, 0]);
    

    where you only set default value conditionally if local storage is empty

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search