I've started to learn web development recently and this is making my head hurt. I made a simple todo app where you can add an item with id and value to an array and delete the item from the array. Now I want whenever the site refreshes, I want the items to be there. I made a testing array and for some reason, when refreshed, it works but not with data
.
const [items, setItems] = useState([]);
const testarr = [{id: 1, value: "a"}, {id: 2, value: "b"}];
useEffect(() => {
const data = JSON.parse(localStorage.getItem("items"));
console.log(data);
// setItems(testarr);
setItems(data);
}, [])
useEffect(() => {
window.localStorage.setItem("items", JSON.stringify(items));
}, [items]);
What I came up with:
useEffect(() => {
const data = JSON.parse(localStorage.getItem("items"));
data.forEach(element => {
setItems(oldarr => [...oldarr, element]);
});
}, [])
The problem is, if I keep refreshing the site fast enough, eventually the array will become empty again. I don't understand, when refreshed it logs data
just fine but when I want to setItems(data)
that doesn't work. Does anyone know why it behaves like this?
CodePudding user response:
setState is an asynchronous function, this implies that this code snippet below will always run with items as empty arrays before items receive data from "setItems(data)".
useEffect(() => {
window.localStorage.setItem("items", JSON.stringify(items));
}, [items]);
maybe, you can check if items isn't a empty array before store
useEffect(() => {
if (Array.isArray(items) && items.length > 0) {
window.localStorage.setItem("items", JSON.stringify(items));
}
}, [items]);
CodePudding user response:
Please try localStorage without window Object
For example:
useEffect(() => {
localStorage.setItem("items", JSON.stringify(items));
}, [items]);
CodePudding user response:
Try this instead
const [items, setItems] = useState([]);
const testarr = [{id: 1, value: "a"}, {id: 2, value: "b"}];
useEffect(() => {
const data = JSON.parse(localStorage.getItem("items"));
if (!Array.isArray(data)) {
localStorage.setItem("items", JSON.stringify(items));
return;
}
setItems(() => [...data]);
}, [])
CodePudding user response:
Issue
The below useEffect
runs every time items
changes, but also on mount. And on mount items
is equal to []
, that array given to useState
in the beginning. That's why the localStorage
is being set to []
.
useEffect(() => {
window.localStorage.setItem("items", JSON.stringify(items));
}, [items]);
Solution
One way to solve this issue is to change your state definition as below, so when there is data in the localStorage
you pick it:
const [items, setItems] = useState(!localStorage.getItem("items") ? [] : JSON.parse(localStorage.getItem("items")));
And at this point, you can remove the below two useEffect
s you have there:
useEffect(() => {
const data = JSON.parse(localStorage.getItem("items"));
data.forEach(element => {
setItems(oldarr => [...oldarr, element]);
});
}, [])
useEffect(() => {
const data = JSON.parse(localStorage.getItem("items"));
console.log(data);
// setItems(testarr);
setItems(data);
}, [])