I try to get the updated localstorage
state called darkmode
with useContext
but when I console log it in functions inside App.tsx
it gives the default value set inside Context.js
:
App.tsx:
const { darkmode } = useContext(Context);
React.useEffect(() => {
console.log(darkmode);
}, []);
Context.js:
import { useState, useEffect, createContext } from "react";
const Context = createContext();
function ContextProvider({ children }) {
const [darkmode, setDarkmode] = useState(false);
// Local Storage: setting & getting data
useEffect(() => {
const darkmode = JSON.parse(localStorage.getItem("darkmode"));
if (darkmode) {
setDarkmode(darkmode);
}
}, []);
useEffect(() => {
localStorage.setItem("darkmode", JSON.stringify(darkmode));
}, [darkmode]);
const toggleDarkmode = () => {
setDarkmode((prev) => !prev);
};
return (
<Context.Provider
value={{
darkmode,
toggleDarkmode,
}}
>
{children}
</Context.Provider>
);
}
export { ContextProvider, Context };
Local storage:
The console logs:
How can I get the latest updated localstorage
state with react context?
The problem is caused by the below useEffect
and how you are initially setting the state:
useEffect(() => {
localStorage.setItem("darkmode", JSON.stringify(darkmode));
}, [darkmode]);
The above useEffect
runs every time darkmode
changes, but also on mount. And on mount, darkmode
is equal to false
, the value given to useState
. So the localStorage
is set to false
, overwriting anything previously registered.
A solution is to change how you are setting the state, so you pick what's in the localStroge
if there is something:
const [darkmode, setDarkmode] = useState(!localStorage.getItem("darkmode") ? false : JSON.parse(localStorage.getItem("darkmode")));
Or, in case you don't wanna access to the local storage in useState
, which can cause issues for frameworks that render both on the client and server, like Next.js, you can change your useEffect
with one additional state:
const [firstRender, setFirstRender] = useState(true);
useEffect(() => {
// This allows the `useEffect` that fetches data from local storage to run first:
if(firstRender){
setFirstRender(false);
return;
}
localStorage.setItem("darkmode", JSON.stringify(darkmode));
}, [darkmode, firstRender]);
Also, to log darkmode
changes, consider adding it to the dependency array of useEffect
:
React.useEffect(() => {
console.log(darkmode);
}, [darkmode]);