There is a button that toggles dark and light mode, and the state of what mode the page is on is saved in localStorage
. However, I cannot change the initial value of the state (dark
) and I don't know why. This is done in a useEffect
function but no matter what the value of dark
is, it is always set to its initial value of false
.
How do I set the value of the localStorage
to the dark
state?
function Mode() {
const [dark, setDark] = useState(false);
// localStorage.removeItem("dark");
const onClick = () => {
if (dark) {
setDark(false);
document.querySelector("body").classList.remove("dark");
} else {
setDark(true);
document.querySelector("body").classList.add("dark");
}
localStorage.setItem("dark", dark);
};
const localDark = JSON.parse(localStorage.getItem("dark"));
useEffect(() => {
if (localDark !== null) {
setDark(!JSON.parse(localStorage.getItem("dark"))); // this is what does not change the value of dark
onClick();
}
}, []);
return (
<div onClick={onClick} className="mode">
{dark ? <Light /> : <Dark />}
</div>
);
}
CodePudding user response:
Directly use the value from localStorage
in useState
as the default. useEffect
is unnecessary here.
const [dark, setDark] = useState(JSON.parse(localStorage.getItem("dark")));
document.body.classList.toggle('dark', dark);
The click event handler should set the localStorage
dark value to the logical complement of the current value.
const onClick = () => {
localStorage.setItem("dark", !dark);
setDark(!dark);
};
CodePudding user response:
Use a function to initialize the state from local storage. Update the storage and the body's class on init, and when dark
state changes:
const getLocalDark = () => !!JSON.parse(localStorage.getItem("dark"));
function Mode() {
const [dark, setDark] = useState(getLocalDark);
const onClick = () => {
setDark(d => !d);
};
useEffect(() => {
const classList = document.querySelector("body").classList;
if (dark) classList.add("dark");
else classList.remove("dark");
localStorage.setItem("dark", dark);
}, [dark]);
return (
<div onClick={onClick} className="mode">
{dark ? <Light /> : <Dark />}
</div>
);
}
CodePudding user response:
To set the value of localStorage to the dark state, you need to set the value of dark to true in your useEffect function.
useEffect(() => {
if (localDark !== null) {
setDark(true); // set dark to true
onClick();
}
}, []);
CodePudding user response:
Perhaps you'd be interested in a useLocalStorage
hook. Here's how that can be implemented:
export const useLocalStorage = (key, initialState) => {
const [value, setValue] = useState(() => {
// Initialize with the value in localStorage if it
// already exists there.
const data = localStorage.getItem(key);
// Otherwise, initialize using the provided initial state.
return data ? JSON.parse(data) : initialState;
});
// Each time "value" is changed using "setValue", update the
// value in localStorage to reflect these changes.
useEffect(() => {
localStorage.setItem(key, JSON.stringify(value));
}, [value]);
return [value, setValue];
};
This hook syncs the value seen in localStorage
with the value stored in memory under the value
variable.
The usage looks like this (almost identical to regular useState
):
export const Counter = () => {
const [count, setCount] = useLocalStorage('count', 0);
return (
<div>
<p>{count}</p>
<button
onClick={() => {
setCount((prev) => prev 1);
}}>
Increase count
</button>
</div>
);
};
However, the main caveat of this hook is that it's only really meant to be used in one component. That means, if you change the value from light
to dark
in one component using this hook, any other components using it won't be updated. So instead, you should look into using a similar implementation of what is demonstrated above, but using the React Context API. That way, you'll ensure your in-memory values are in sync with those stored in localStorage
. Theming is one of the main uses of the Context API.
Good luck! :)