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

React任务勾选后无法显示在已完成区域及持久化问题

问题修复方案

核心问题分析

  • 已完成任务存储为嵌套数组:handlerCompleted中用slice获取任务,返回的是数组而非单个字符串,导致completed数组元素是嵌套结构,渲染时无法正确显示文本。
  • 子组件状态未同步父组件props:Completed组件的completedTasks仅在初始化时接收父组件的completed,后续父组件更新时未同步,导致新添加的已完成任务不触发子组件渲染。
  • 全局状态未持久化:父组件AddItem的tasks和completed状态未从localStorage初始化,页面刷新后父组件数据丢失,仅子组件读取存储会导致数据不一致。

分步修复代码

1. 修复父组件AddItem的任务处理与状态持久化

import { useState, useEffect } from "react";
import Completed from "../completed/Completed";
import TodoList from "../todoList/TodoList";
import "./additems.css";

const AddItem = () => {
    // 从localStorage初始化状态,避免刷新丢失
    const [tasks, setTasks] = useState(() => {
        return JSON.parse(localStorage.getItem("tasks")) || [];
    });
    const [inputValue, setInputValue] = useState("");
    const [editingIndex, setEditingIndex] = useState(null);
    const [completed, setCompleted] = useState(() => {
        return JSON.parse(localStorage.getItem("completedTasks")) || [];
    });

    const currentDate = new Date().toLocaleString('en-US', { weekday: 'long' });

    // 监听tasks变化,同步到localStorage
    useEffect(() => {
        localStorage.setItem("tasks", JSON.stringify(tasks));
    }, [tasks]);

    // 监听completed变化,同步到localStorage
    useEffect(() => {
        localStorage.setItem("completedTasks", JSON.stringify(completed));
    }, [completed]);

    const handlerInputChange = (e) => {
        let textInp = e.target.value;
        if (textInp.length <= 70) {
            setInputValue(textInp);
        }
    }

    const handlerAdd = (e) => {
        e.preventDefault();
        if (inputValue.trim() === "") {
            return;
        }
        setTasks([...tasks, `${inputValue} - ${currentDate}`]);
        setInputValue("");
        setEditingIndex(tasks.length);
    }

    const handlerDelete = (index) => {
        const newTasks = [...tasks];
        newTasks.splice(index, 1);
        setTasks(newTasks);
    }

    const handlerCompleted = (index) => {
        const newTasks = [...tasks];
        // 直接取单个任务,而非slice返回的数组
        const completedTask = newTasks[index];
        setCompleted([...completed, completedTask]);
        setTasks(newTasks.slice(0, index).concat(newTasks.slice(index + 1)));
    }

    const renderItems = (arr) => {
        const items = arr.map((item, index) => {
            if (index === editingIndex) {
                return (
                    <li key={index} className="list_save">
                        <div className="date_number">
                            <p>{index + 1}</p>
                        </div>
                        <input
                            maxLength={50}
                            className="task_value"
                            type="text"
                            defaultValue={item}
                            onChange={(e) => {
                                const newTasks = [...tasks];
                                newTasks[index] = e.target.value;
                                setTasks(newTasks);
                            }}
                        />
                        <button
                            onClick={() => setEditingIndex(null)}
                            className="save_task"
                        >Save</button>
                    </li>
                )
            } else {
                return (
                    <li key={index} className="list_edit">
                        <input type="checkbox"
                            className="checkbox_completed"
                            onClick={() => handlerCompleted(index)} />
                        <p>{item}</p>
                        <button
                            onClick={() => setEditingIndex(index)}
                            className="edit_task"
                        >Edit</button>
                        <button
                            onClick={() => handlerDelete(index)}
                            className="delete_task"
                        >Delete</button>
                    </li>
                );
            }
        })
        return (
            <ol className="items_list">
                {items}
            </ol>
        )
    }

    const items = renderItems(tasks);
    return (
        <div>
            <form
                className="form_add"
                onSubmit={handlerAdd}
            >
                <input className="input_add"
                    maxLength={50}
                    type="text"
                    value={inputValue}
                    onChange={handlerInputChange} />
                <button className="btn_add" type="submit">Add</button>
            </form>
            <TodoList task={items} />
            <Completed completed={completed} />
        </div>
    )
}
export default AddItem;

2. 修复子组件Completed的状态同步逻辑

import { useState, useEffect } from "react";
import "./completed.css";

const Completed = (props) => {
    const { completed } = props;
    const [completedTasks, setCompletedTasks] = useState(completed);

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

    return (
        <div className="completed_list">
            <h3>Completed</h3>
            <div className="line_completed"></div>
            {completedTasks.map((task, index) => {
                return (
                    <ul key={index}>
                        <li>{task}</li>
                    </ul>
                )
            })}
        </div>
    )
}
export default Completed;

修复说明

  1. 嵌套数组问题:将newTasks.slice(index, index + 1)改为newTasks[index],直接获取单个任务字符串,确保completed数组存储的是文本而非嵌套数组。
  2. props同步问题:在Completed组件中添加监听completed props的useEffect,父组件更新已完成任务时,子组件自动同步状态并重新渲染。
  3. 数据持久化统一:将localStorage的读写逻辑移到父组件,初始化状态时从存储读取,状态变化时同步存储,确保父组件和子组件的数据一致,刷新后不会丢失。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.29 20:35:25