I have a custom hook to manage localStorage
like below:
import { useEffect, useState } from 'react'
export default function useLocalStorage(key, initValue) {
const [state, setState] = useState(() => {
const value = localStorage.getItem(key);
if (value !== null) {
return JSON.parse(value);
}
localStorage.setItem(key, JSON.stringify(initValue));
return initValue;
})
useEffect(() => {
localStorage.setItem(key, state);
}, [key, state])
return [state, setState];
}
This custom hook is declared to use the same localstorage key for two or more ponents in one screen. e,g.
const [state, setState] = useLocalStorage('key', false);
At this time, since ponents using useLocalStorage
want to work according to the state change of localStorage
, we try to use the state returned by useLocalStorage
as the second parameter of useEffect
.
useEffect(() => {
foobar()
}, [state]);
I expected the foobar()
function in useEffect
to work when the "state" changes in all ponents that declared this useEffect
. However, foobar()
only worked for one ponent out of several ponents using this same useEffect
.Why this is happening? How do I get it to work the way I want it to?
I have a custom hook to manage localStorage
like below:
import { useEffect, useState } from 'react'
export default function useLocalStorage(key, initValue) {
const [state, setState] = useState(() => {
const value = localStorage.getItem(key);
if (value !== null) {
return JSON.parse(value);
}
localStorage.setItem(key, JSON.stringify(initValue));
return initValue;
})
useEffect(() => {
localStorage.setItem(key, state);
}, [key, state])
return [state, setState];
}
This custom hook is declared to use the same localstorage key for two or more ponents in one screen. e,g.
const [state, setState] = useLocalStorage('key', false);
At this time, since ponents using useLocalStorage
want to work according to the state change of localStorage
, we try to use the state returned by useLocalStorage
as the second parameter of useEffect
.
useEffect(() => {
foobar()
}, [state]);
I expected the foobar()
function in useEffect
to work when the "state" changes in all ponents that declared this useEffect
. However, foobar()
only worked for one ponent out of several ponents using this same useEffect
.Why this is happening? How do I get it to work the way I want it to?
You should set up an event listener for storage changes in order to have the updated value. See the code below and the note about storage event:
import { useEffect, useState } from 'react'
export default function useLocalStorage(key, initValue) {
const [state, setState] = useState(() => {
const value = localStorage.getItem(key);
if (value !== null) {
return JSON.parse(value);
}
localStorage.setItem(key, JSON.stringify(initValue));
window.dispatchEvent(new Event("storage"));
return initValue;
});
useEffect(() => {
localStorage.setItem(key, state);
window.dispatchEvent(new Event("storage"));
}, [key, state]);
useEffect(() => {
const listenStorageChange = () => {
setState(() => {
const value = localStorage.getItem(key);
if (value !== null) {
return JSON.parse(value);
}
localStorage.setItem(key, JSON.stringify(initValue));
window.dispatchEvent(new Event("storage"));
return initValue;
});
};
window.addEventListener("storage", listenStorageChange);
return () => window.removeEventListener("storage", listenStorageChange);
}, []);
return [state, setState];
}
You might wonder why this dispatchEvent
is used. Well here is what MDN says about storage event:
The
storage
event of theWindow
interface fires when a storage area (localStorage
) has been modified in the context ofanother document
.
This dispatchEvent
is needed as we need to listen to changes in the same document as well.