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> ); }
核心问题分析
- 直接修改props对象违反React不可变原则:
Todo组件中直接修改todo.done = true,没有触发React状态更新,App中的todos数组无法感知变化,导致DoneTodos拿不到最新的任务状态。 - 临时变量无法保留状态:
displayTodos是组件内的普通变量,每次组件重新渲染都会被重置为空数组,即使调用setState(true),渲染时也无法拿到筛选后的任务。 - 状态逻辑冗余:用
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
相关产品推荐
相关产品推荐

