I'm connecting user authorization with the backend. When I receive accessToken
, I store it in
local storage. When it expires, I remove it from local storage.
Saving and removing from local storage happens in my service file, and the below code is a part of my context file from which I manage my global session state.
The first useEffect
keeps the user logged in when the page is reloaded. In the second one, I wanted to
automatically log the user out whenever one of the endpoints returns 401 unauthorized
(so when
accessToken
is removed from local storage).
However, the second one does not reload when the token is removed. Even if I put there a console.log
, it doesn't show up in the console. It just doesn't even run.
Why is useEffect
not reacting to local storage item change?
const [sessionState, setSessionState] = useState<SessionStateType>({
status: "anon",
accessToken: null,
});
useEffect(() => {
const token = localStorage.getItem("accessToken");
if (token) {
setSessionState({ status: "auth", accessToken: token });
}
}, []);
const token = localStorage.getItem("accessToken");
useEffect(() => {
if (!token) {
setSessionState({ status: "anon", accessToken: null });
}
}, [token]);
CodePudding user response:
There is no listening to local storage changes in your code, and it's not the behavior by default. And even if it was the case, you need to somehow re-render your component to have your useEffect
with token
as a dependency to re-run.
A solution is to set up an event listener for storage
in your useEffect
, and remove that token
that's outside, like so:
// const token = localStorage.getItem("accessToken");
useEffect(() => {
// on page load
const token = localStorage.getItem("accessToken");
if (!token) {
setSessionState({ status: "anon", accessToken: null });
}
// when the storage change
window.addEventListener("storage", () => {
const token = localStorage.getItem("accessToken");
if (!token) {
setSessionState({ status: "anon", accessToken: null });
}
});
}, []);
But for this event to run, anytime you clear the storage, immediately call a dispatchEvent
like so:
localStorage.removeItem("accessToken");
window.dispatchEvent(new Event("storage"));
That last step is needed because storage event works this way:
The
storage
event of theWindow
interface fires when a storage area (localStorage
) has been modified in the context ofanother document
.
So this dispatchEvent
is needed as we need to listen to changes in the same document as well.