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

React JS组件功能异常求助:DoneTodos与Todo组件问题排查

问题:DoneTodos组件按钮失效,无法渲染已完成任务列表

开发了DoneTodos和Todo两个React组件,DoneTodos调用Todo组件,预期点击按钮后渲染出done字段为true的任务列表,但按钮完全无法正常工作。以下是原代码及修复方案:


原代码展示

Todo.jsx

export default function Todo({todo}) {
    function handleCheck() {
        todo.done = true;
        console.log(todo);
    }

    return (
        <>
            <input type="checkbox" onChange={handleCheck}/>
            <span>
                {todo.text}
            </span>
        </>
    );
}

DoneTodos.jsx

import Todo from "./Todo.jsx";
import {useState} from "react";

// TODO: Button doesn't work at all. Do smth about it

export default function DoneTodos({todos}){
    const [state, setState] = useState(false);
    let displayTodos = [];

    function handleDoneTodos() {
        for(let i = 0; i < todos.length; i++) {
            if (todos[i].done === true) {
                displayTodos.push(todos[i]);
            }
        }
        setState(true);
    }

    if (state !== false) {
        return (
            <>
                <ul>
                    {
                        displayTodos.map((todo, key) => (
                            <li key={key}>
                                <Todo todo={todo}></Todo>
                            </li>
                        ))
                    }
                </ul>
            </>
        );
    }
    return (
        <button onClick={handleDoneTodos}>
            Show done tasks
        </button>
    );
}

App.jsx

import './App.css'
import CreateTodos from "./components/CreateTodos.jsx";
import TodoList from "./components/TaskList.jsx";
import {useState} from "react";
import DoneTodos from "./components/DoneTodos.jsx";

export default function App() {
    const [todos, setTodos] = useState([]);

    return (
       <>
           <CreateTodos todos={todos} setTodos={setTodos} />
           <TodoList todos={todos} />
           <DoneTodos todos={todos} />
       </>
    )
}

CreateTodos.jsx

export default function CreateTodos({todos, setTodos}) {
    let onEnterClick = event => {
        if (event.key === 'Enter') {
            setTodos([
                ...todos,
                {
                    text: event.target.value,
                    done: false
                } // save todo to todos list
            ]);
            event.target.value = ''; // reset input
        }
    }

    return (
        <p>
            <input type="text" placeholder="Enter your task here!" onKeyDown={onEnterClick} />
        </p>

    );
}

核心问题分析

  1. 直接修改props对象违反React不可变原则:Todo组件中直接修改todo.done = true,没有触发React状态更新,App中的todos数组无法感知变化,导致DoneTodos拿不到最新的任务状态。
  2. 临时变量无法保留状态:displayTodos是组件内的普通变量,每次组件重新渲染都会被重置为空数组,即使调用setState(true),渲染时也无法拿到筛选后的任务。
  3. 状态逻辑冗余:用state控制显示按钮/列表的逻辑没问题,但没有正确基于最新的todos筛选任务,而是在点击时一次性存到临时变量,无法响应后续任务状态变化。

修复后的代码

1. Todo.jsx(修正状态更新逻辑)

export default function Todo({ todo, onToggleDone }) {
  function handleCheck() {
    // 通过父组件传递的回调更新状态,不直接修改todo对象
    onToggleDone(todo);
  }

  return (
    <>
      {/* 绑定checkbox的checked状态,同步任务完成状态 */}
      <input type="checkbox" checked={todo.done} onChange={handleCheck} />
      <span>{todo.text}</span>
    </>
  );
}

2. DoneTodos.jsx(简化筛选逻辑,支持切换显示)

import Todo from "./Todo.jsx";
import { useState } from "react";

export default function DoneTodos({ todos, onToggleDone }) {
  const [showDone, setShowDone] = useState(false);

  // 实时从props中筛选已完成任务,无需临时变量
  const doneTodos = todos.filter(todo => todo.done === true);

  function handleToggleShow() {
    setShowDone(!showDone);
  }

  if (showDone) {
    return (
      <>
        <button onClick={handleToggleShow}>Hide done tasks</button>
        <ul>
          {doneTodos.map((todo, key) => (
            <li key={key}>
              <Todo todo={todo} onToggleDone={onToggleDone} />
            </li>
          ))}
        </ul>
      </>
    );
  }
  return (
    <button onClick={handleToggleShow}>
      Show done tasks
    </button>
  );
}

3. App.jsx(统一管理状态更新)

import './App.css'
import CreateTodos from "./components/CreateTodos.jsx";
import TodoList from "./components/TaskList.jsx";
import { useState } from "react";
import DoneTodos from "./components/DoneTodos.jsx";

export default function App() {
  const [todos, setTodos] = useState([]);

  // 遵循不可变原则更新任务完成状态
  const handleToggleDone = (targetTodo) => {
    setTodos(todos.map(todo => {
      if (todo === targetTodo) {
        return { ...todo, done: !todo.done };
      }
      return todo;
    }));
  };

  return (
    <>
      <CreateTodos todos={todos} setTodos={setTodos} />
      <TodoList todos={todos} onToggleDone={handleToggleDone} />
      <DoneTodos todos={todos} onToggleDone={handleToggleDone} />
    </>
  )
}

补充:TodoList.jsx(适配回调传递)

import Todo from "./Todo.jsx";

export default function TodoList({ todos, onToggleDone }) {
  return (
    <ul>
      {todos.map((todo, key) => (
        <li key={key}>
          <Todo todo={todo} onToggleDone={onToggleDone} />
        </li>
      ))}
    </ul>
  );
}

修复关键点说明

  • 严格遵循React状态不可变原则:所有状态更新都通过setTodos完成,返回新的数组/对象,确保React能检测到变化并重新渲染组件。
  • 单向数据流:子组件不直接修改状态,而是调用父组件传递的回调函数,由父组件统一管理状态,符合React设计理念。
  • 实时筛选任务:DoneTodos直接通过filter从props的todos中筛选已完成任务,确保每次组件渲染都能拿到最新数据。
  • 增强交互性:按钮支持切换显示/隐藏已完成任务,提升用户体验。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 16:03:16