React Todo List页面刷新时Local Storage被清空的问题求助
问题解决:React Todo List刷新后Local Storage被清空的问题
问题根源
你的代码存在执行顺序导致的覆盖问题:
- 组件初始化时,
useState将todos设为空数组[]。 - 初始渲染完成后,第二个依赖
todos和status的useEffect会先执行,此时todos是空数组,saveLocalTodos会把空数组存入Local Storage,直接覆盖原本存储的数据。 - 之后第一个空依赖的
useEffect才执行getLocalTodos,但此时Local Storage已经被清空,只能读取到空数组,最终导致每次刷新都丢失数据。
解决方案
方案1:直接从Local Storage初始化useState
这是最简洁的方式,跳过初始空数组阶段,避免不必要的存储操作:
// 直接从Local Storage读取初始值,不存在则设为空数组 const [todos, setTodos] = useState(() => { const savedTodos = localStorage.getItem('todos'); return savedTodos ? JSON.parse(savedTodos) : []; }); // 只保留处理过滤和存储的useEffect useEffect(() => { filterHandler(); saveLocalTodos(); }, [todos, status]); const filterHandler = () => { switch(status){ case 'completed': setFilteredTodos(todos.filter(todo => todo.completed)) break; case 'todo': setFilteredTodos(todos.filter(todo => !todo.completed)) break; default: setFilteredTodos(todos); break; } } const saveLocalTodos = () => { localStorage.setItem("todos", JSON.stringify(todos)) } // 移除原来的getLocalTodos和对应的useEffect,因为初始值已从Local Storage获取
方案2:调整useEffect执行逻辑(保留原结构)
如果不想修改useState初始化方式,可以给第二个useEffect加判断,避免初始空数组覆盖Local Storage:
const [todos, setTodos] = useState([]); useEffect(()=>{ getLocalTodos(); }, []) useEffect(() => { filterHandler(); // 只有当todos不是初始空数组时才存储,避免覆盖原有数据 if (todos.length > 0) { saveLocalTodos(); } }, [todos, status]); // 其余函数保持不变 const filterHandler = () => { switch(status){ case 'completed': setFilteredTodos(todos.filter(todo => todo.completed)) break; case 'todo': setFilteredTodos(todos.filter(todo => !todo.completed)) break; default: setFilteredTodos(todos); break; } } const saveLocalTodos = () => { localStorage.setItem("todos", JSON.stringify(todos)) } const getLocalTodos = () => { if(localStorage.getItem('todos') === null){ localStorage.setItem("todos", JSON.stringify([])); }else{ let todoLocal = JSON.parse(localStorage.getItem("todos")) setTodos(todoLocal) } }
额外优化建议
filterHandler里调用setFilteredTodos会触发组件重新渲染,你可以用useMemo计算过滤后的列表,减少不必要的渲染:
const filteredTodos = useMemo(() => { switch(status){ case 'completed': return todos.filter(todo => todo.completed); case 'todo': return todos.filter(todo => !todo.completed); default: return todos; } }, [todos, status]);
这样就不需要单独的filteredTodos状态和filterHandler函数了。
内容的提问来源于stack exchange,提问作者user20196028
相关产品推荐
相关产品推荐

