Error: localStorage
data keeps resetting on page reload. Despite of being updated to localStorage before.
If I add new todo item, it is added to localStorage. But the updated items disappears after page reload and I get initial state value(todos).
source code: https://www.codepile.net/pile/J9L4xL9g
// App.js
// Error: localStorage data keeps resetting on page reload. Despite of being updated to localStorage before.
// If I add new todo item, it is added to localStorage. But it disappears after page reload and I get initial state value(todos). I don't know where is the problem here!
import { useState, useEffect } from "react";
function App() {
const [todos, setTodos] = useState([{ id: 123, text: "todo 1 demo" }]);
const [todo, setTodo] = useState("");
function handleSubmit(e) {
e.preventDefault();
const newTodo = {
id: new Date().getTime(),
text: todo
};
setTodos([...todos].concat(newTodo));
setTodo("");
}
useEffect(() => {
const temp = localStorage.getItem("todos");
const loadedTodos = JSON.parse(temp);
if (loadedTodos) {
setTodos(loadedTodos);
}
}, []);
useEffect(() => {
localStorage.setItem("todos", JSON.stringify(todos));
}, [todos]);
return (
<div className="App">
<form onSubmit={handleSubmit}>
<input
type="text"
onChange={(e) => setTodo(e.target.value)}
value={todo}
/>
<button>Add todo</button>
</form>
{todos.map((todo) => (
<div key={todo.id}>
<div>{todo.text}</div>
</div>
))}
</div>
);
}
export default App;
CodePudding user response:
You need to check the length of todos
array in your useEffect
function.
the callback function on useEffect will fire on each change of dependencies update
so in the first time that todos state set with its initial values, useEffect callback is fired and this is why after each reload you lose your data
useEffect(() => {
if (todos.length > 1) localStorage.setItem("todos", JSON.stringify(todos));
}, [todos]);
You can fix your issue by adding this simple if condition
CodePudding user response:
The issue you were running into is that your useEffect
to set the todos from the storage happens at a bad time in comparison with the one to set it.
So, instead of using an effect to grab the todos, get them synchronously with the state. This style of useState
with a function argument, allows you to do a more expensive calculation for the initial creation of the state, so the function will only ever be called on initial component mounting.
const [todos, setTodos] = useState(
() =>
JSON.parse(localStorage.getItem("todos")) || [
{ id: 123, text: "todo 1 demo" }
]
);
useEffect(() => {
localStorage.setItem("todos", JSON.stringify(todos));
}, [todos]);
In this way, you can get rid of the extra effect, and prevent a flash of incorrect UI between the initial code run and grabbing the todos from local storage.
https://codesandbox.io/s/eloquent-babycat-z86dts?file=/src/App.js