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

React Todo列表切换后useState状态丢失问题求助

React Todo列表切换后删除项恢复问题修复

问题根源

  1. 直接操作DOM导致状态与视图脱节:你通过event.target.parentElement.remove()手动删除DOM节点,但React是数据驱动视图的框架,这种操作会让toDoItems状态和实际DOM内容不一致,切换列表后组件重新渲染时,会根据旧状态生成DOM,已删除的项自然恢复。
  2. 子组件状态未同步父组件更新:toDoItems用useState(listData)初始化后,不会自动响应父组件listData的变化,切换列表回来时,子组件还是用旧的状态数据渲染。
  3. useEffect依赖缺失+手动DOM操作:原useEffect空数组只执行一次,无法响应状态变化;手动创建li/button元素的方式违背React渲染逻辑,极易引发视图与状态不一致。
  4. 删除逻辑取值不可靠:用event.target.parentElement.value获取待删除项不合理,li元素的value属性并非存储文本内容的正确方式。

修复后的Content组件代码

import React, { useState, useEffect } from "react";
import './Content.css';

export const Content = ({ list, listData, setData }) => {
    const [inputText, setInputText] = useState('');
    const [toDoItems, setToDoItems] = useState(listData);

    // 监听父组件listData变化,同步更新子组件状态
    useEffect(() => {
        setToDoItems(listData);
    }, [listData]);

    const handleChange = (event) => {
        setInputText(event.target.value);
    }

    // 直接传入待删除项,避免DOM取值错误
    const deleteItem = (itemToDelete) => {
        setToDoItems(prevItems => prevItems.filter(item => item !== itemToDelete));
    }

    const addItem = () => {
        if (inputText.trim() !== '') {
            setToDoItems(prevItems => [...prevItems, inputText.trim()]);
            setInputText('');
        }
    }

    const addItemAfterKeyDown = (event) => {
        if (event.keyCode === 13) {
            addItem();
        }
    }

    const handleClick = (event) => {
        if (event.target.tagName === 'LI') {
            event.target.classList.toggle('done');
        }
    }

    const saveButton = () => {
        setData(list, toDoItems);
    }

    return (
        <div className="content">
            <div className="header">
                <h1>{list}</h1>
            </div>
            <div className="listContent">
                <h2>To Do:</h2>
                <div className="listItems ">
                    {/* 完全由React状态驱动渲染列表 */}
                    <ol onClick={handleClick} className="toDoList">
                        {/* 默认项 */}
                        <li>Grab Coffee 
                            <button onClick={() => deleteItem('Grab Coffee')} className="deleteButton" type="button">&#60;</button> 
                        </li>
                        <li>Make Breakfast 
                            <button onClick={() => deleteItem('Make Breakfast')} className="deleteButton" type="button">&#60;</button> 
                        </li>
                        <li>Homework 
                            <button onClick={() => deleteItem('Homework')} className="deleteButton" type="button">&#60;</button> 
                        </li>
                        {/* 渲染自定义添加的项 */}
                        {toDoItems.map((item, index) => (
                            <li key={index}>
                                {item}
                                <button onClick={() => deleteItem(item)} className="deleteButton" type="button">&#60;</button>
                            </li>
                        ))}
                    </ol>
                </div>
                <div className="addItems">
                    {/* 受控组件,绑定状态 */}
                    <input 
                        onKeyDown={addItemAfterKeyDown} 
                        onChange={handleChange} 
                        type="text" 
                        id="addItems"
                        value={inputText}
                    />
                    <br/>
                    <button onClick={addItem} className="addButton" type="button">Add Item</button>
                    <button onClick={saveButton} className="addButton" type="button">Save List</button>
                </div>
            </div>
        </div>
    );
}

关键修复点说明

  • 数据驱动视图:移除所有手动操作DOM的代码,列表项完全由toDoItems状态通过map渲染,确保视图与状态完全同步。
  • 同步父组件props:添加useEffect监听listData变化,父组件列表数据更新时自动同步子组件状态,切换列表时能加载最新数据。
  • 受控组件优化:输入框改为受控组件,绑定inputText状态,避免手动操作DOM清空输入框。
  • 可靠删除逻辑:删除时直接传入目标项,不再依赖DOM元素的value属性,避免取值错误。
  • 移除无效DOM操作:删除原空依赖useEffect,不再手动添加列表项,完全由React负责渲染流程。

额外优化建议

  • 给每个todo项生成唯一ID(比如用uuid库),替换index作为key,避免列表项顺序变化时出现渲染异常。
  • 将默认的3项合并到listData或单独维护默认数组,统一渲染逻辑,减少代码重复。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 00:15:07