'Change in localstorage not triggering event listener
I have React component. Initially I set some localStorage in UseEffect. Moreover I add event listener. After clicking on text it changes the localStorage value but event listener does not triggering, why?
import React, { useEffect } from "react";
export default function App() {
useEffect(() => {
window.localStorage.setItem("item 1", 'val 1');
window.addEventListener('storage', () => {
alert('localstorage changed!')
})
}, []);
const getData = () => {
localStorage.setItem("item", "val chamged");
};
return (
<div className="App">
<h1 onClick={getData}>Change localstorage value</h1>
</div>
);
}
Solution 1:[1]
There are two things wrong.
- change
onClick={getData()}onClick={getData} - From the doc(https://developer.mozilla.org/en-US/docs/Web/API/Window/storage_event).
The storage event of the Window interface fires when a storage area (localStorage or sessionStorage) has been modified in the context of another document.Note the last sentence that says it won't be fired in the same document. You can see that if you open https://codesandbox.io/s/spring-browser-89con in 2 tabs in same browser, the alert will start coming.
Solution 2:[2]
The Storage event is triggered when there is a change in the window's storage area.
Note: The storage event is only triggered when a window other than itself makes the changes.
You can see more details and demo: storage Event
The storage event handler will only affect other windows. Whenever something changes in one window inside localStorage all the other windows are notified about it and if any action needs to be taken it can be achieved by a handler function listening to the storage event.
Solution 3:[3]
Try with this onclick handler
import React, { useEffect } from "react";
export default function App() {
useEffect(() => {
window.localStorage.setItem("item 1", 'val 1');
window.addEventListener('storage', () => {
alert('localstorage changed!')
})
}, []);
const getData = () => {
localStorage.setItem("item", "val chamged");
};
return (
<div className="App">
<h1 onClick={()=>getData()}>Change localstorage value</h1>
</div>
);
}
Solution 4:[4]
I will suggest a solution. It might be an ugly solution, but it is the only solution that I have found to achieve it from the same window. (for some reasons I need to get the change :) )
I've used the MutationObserver class, for example to get any changes on the dark mode value:
const setTheme = (value) => {
let htmlClasses = document.querySelector('html').classList
if (value == true) {
htmlClasses.add('dark');
} else {
htmlClasses.remove('dark');
}
window.localStorage.setItem('dark', value)
}
Here I am adding a class each time the toggle button has been activated, then I configure an observer on the html classes like this:
var observer = new MutationObserver(function (mutations) {
mutations.forEach(function (mutation) {
console.log(mutation);
if (mutation.attributeName == "class") {
// Do what you want here
}
});
});
var config = {
attributes: true
};
observer.observe(document.querySelector('html'), config);
You can see also an other example (more useful for you) that I've found on github here
Solution 5:[5]
To update the localStorage in the same window, you need to dispatch a storage event.
I changed some of the values from the original question to update the same cookie that is initially set.
Don't forget that event listeners need to be removed when the component unmounts and localStorage items should have a same site and secure attribute!
import React, { useEffect } from "react";
export default function App() {
//Set localStorage item when the component mounts and add storage event listener
useEffect(() => {
const alertMessage = () => {
alert('localStorage changed!');
}
window.localStorage.setItem("item", 'val 1', { sameSite: "strict", secure: true });
window.addEventListener('storage', alertMessage);
//Remove the event listener when the component unmounts
return () => {
window.removeEventListener("storage", alertMessage);
}
}, []);
//Update the localStorage onClick
const updateData = () => {
localStorage.setItem("item", "val changed", { sameSite: "strict", secure: true });
window.dispatchEvent(new Event("storage")); //This is the important part
};
return (
<div className="App">
<h1 onClick={updateData}>Change localStorage value</h1>
</div>
);
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Subin Sebastian |
| Solution 2 | Mohammad Oftadeh |
| Solution 3 | Kesav |
| Solution 4 | |
| Solution 5 | Rllyyy |
