0

I am setting a localstorage value when the user clicks onto a page in the app training.js and then I want this value to be read by sidebar.js, when the localstorage items is set it only outputs the value in the console.log once the user opens or closes the sidebar, it is not being picked up when the props changes as I would expect by useEffect.

How can I get useEffect to pick up the value when it changes?

training.js

 {typeof window !== 'undefined' && localStorage.setItem(topic.slug, topic.progress)}    

sidebar.js

const Sidebar = () => {
      const structureAndDetails = typeof window !== 'undefined' ? Number(localStorage.getItem('structure-and-details')) : null
      const hydraulics = typeof window !== 'undefined' ? Number(localStorage.getItem('hydraulics')) : null


  useEffect(() => {

   console.log('hydraulics',hydraulics)
  }, [hydraulics])
tom harrison
  • 3,273
  • 11
  • 42
  • 71
  • Does [this codesandbox](https://codesandbox.io/s/wymmko1w28?file=/src/components/Counter.js) help you anyhow? – Sinan Yaman Jan 19 '21 at 08:31
  • localStorage is not reactive and can not be watched on its own (unless you use some library). You can also use a state management ecosystem (React Context API, redux, mobx and etc) to do this – Mahdi Aryayi Jan 19 '21 at 08:35

2 Answers2

1

As noted above, localStorage changes cannot be watched. As a workaround you can dispatch a custom event whenever localStorage is changed:

training.js:

{typeof window !== 'undefined' && localStorage.setItem(topic.slug, topic.progress)}
document.dispatchEvent(new Event('storageChanged'));

sidebar.js

const Sidebar = () => {
  const storageHandler = useCallback(() => {
    const structureAndDetails = typeof window !== 'undefined' ? Number(localStorage.getItem('structure-and-details')) : null
    const hydraulics = typeof window !== 'undefined' ? Number(localStorage.getItem('hydraulics')) : null
    
    console.log('hydraulics',hydraulics)
  }, []);

  useEffect(() => {
    document.addEventListener("storageChanged", storageHandler, false);

    // Don't forget to remove event listener on component unmount
    return () => {
      document.removeEventListener("storageChanged", storageHandler);
    };
  }, [storageHandler]);

  ...
}
oozywaters
  • 1,171
  • 1
  • 8
  • 17
0

You can use a React useEffect hook to "listen" for changes in localStorage, via storage events.

The storage event of the Window interface fires when a storage area (localStorage) has been modified in the context of another document.

Use an useEffect hook to add the listener, handler, and cleanup function.

React.useEffect(() => {
  const checkStorage = () => {
    const myValue = window.localStorage.getItem(topic.slug);

    // handle updated value
  };

  window.addEventListener("storage", checkStorage);

  checkStorage();

  return () => window.removeEventListener("storage", checkStorage);
}, []);

One key caveat though, as italicized in the quotation above and explained in this answer is that storage events are not universally supported and that it likely won't work for an app running in the same window.

Try this following demo and notice that it doesn't work while in the same window, but if you open the sandbox in two windows/tabs it works. I don't think using localStorage is what you want if you want different parts of your app to listen to and handle changes to "app state" from other parts of your app. This is what solutions like the React Context API or Redux are for.

Edit watch-for-prop-changes-with-useeffect

Drew Reese
  • 165,259
  • 14
  • 153
  • 181