You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React.js开发TodoList刷新页面后待办项丢失问题解决

问题根因

刷新后待办项消失是useEffect执行顺序导致的存储覆盖问题:

  • React组件首次挂载时,所有useEffect会严格按照代码中的声明顺序依次执行
  • 你的代码里先声明了依赖list的持久化effect:首次执行时list是初始空数组[],会直接把空值写入LocalStorage,把之前存好的待办数据完全覆盖
  • 等后面声明的读取LocalStorage的effect执行时,读到的已经是刚被覆盖的空数组,自然无法恢复之前的待办内容
修复方案

推荐直接在useState初始化阶段就读取LocalStorage作为list的初始值,删掉单独用来做初始化读取的useEffect,从根源上避免首次渲染空值覆盖存储的问题,这也是React状态持久化的通用最佳实践。

如果你不想调整初始化逻辑,把读取LocalStorage的useEffect移到持久化effect的前面,让读取逻辑先执行也能临时解决问题,但这种方案强依赖effect的声明顺序,后续调整代码时很容易再次出问题,不推荐使用。

修复后代码

import React, { useState, useEffect } from 'react'
import TodoList from './TodoList.jsx'

const AddTodo = () => {
    const [newTodo, setNewTodo] = useState('')
    // 惰性初始化:仅在组件首次渲染时读取一次LocalStorage
    const [list, setList] = useState(() => {
        const storedData = localStorage.getItem('list')
        return storedData ? JSON.parse(storedData) : []
    })
    
    const handleSubmit = () => {
        // 新增空值校验,避免添加空内容/全空格的无效待办
        if (!newTodo.trim()) return
        const todoItem = {
            id: Math.floor(Math.random() * 10000),
            value: newTodo.trim()
        }
        setList([...list, todoItem])
        setNewTodo('')
    }

    // list变化时自动同步到LocalStorage,首次执行时list已是有效初始值,不会覆盖旧数据
    useEffect(() => {
        localStorage.setItem("list", JSON.stringify(list))
    }, [list])
   
    return (
        <div className="input_container">
             <form onSubmit={(e) => e.preventDefault()}>
                <input 
                    type="text"
                    onChange={e => setNewTodo(e.target.value)}
                    className="todo-input" 
                    placeholder="Write Todo.." 
                    value={newTodo} 
                    name="text"
                />
                <button 
                    onClick={handleSubmit} 
                    className="add-todo-button"
                >
                    Add Todo
                </button>
            </form>
            <TodoList list={list} newTodo={newTodo}/>
        </div>
    )
}

export default AddTodo
额外说明
  • 这里用了useState的惰性初始化语法(传函数而不是直接传值),读取LocalStorage的逻辑只会在组件首次渲染时执行一次,相比在effect里读取性能更好
  • 新增的输入trim校验可以避免用户误输入空内容生成无效待办,你可以根据自己的需求决定是否保留
  • 给onClick直接传handleSubmit函数引用即可,不需要额外包一层箭头函数,能减少不必要的重渲染

内容的提问来源于stack exchange,提问作者SwedieK

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.30 02:15:33