React TodoList 通俗讲解|彻底搞懂父子组件通信与状态管理

很多初学 React 的同学绕不明白:子组件为啥不能直接修改父的数据?数组对象更新为什么不能直接改?今天用 TodoList 案例,大白话讲清楚。

先记住 2 条铁律✅ 规则 1:数据统一放在父组件 App 里保管,子组件不能直接动手改这份数据✅ 规则 2:子想要修改数据 → 调用父给的函数,让父自己去改数据

1. 第一块: todos 是什么?

jsx

php 复制代码
const [todos, setTodos] = useState([
    { id: 1, text: '吃饭', completed: false },
    { id: 2, text: '睡觉', completed: false },
    { id: 3, text: '打豆豆', completed: true }
])

把它理解成:App(爸爸)手里拿着一张清单todos,记录所有待办事项。

  • todos = 清单本身(只能看,不能直接改)
  • setTodos = 唯一能修改清单的工具

❌ 错误写法:直接 push、直接修改里面对象。React 看不见改动,页面不会刷新。✅ 正确写法:必须使用 setTodos 换一份全新清单。

每条待办的含义:

  • id:编号,用来区分每一条任务
  • text:任务文字
  • completed:是否完成(true = 完成,false = 未完成)

2. 函数 1:addTodo 添加任务

jsx

vbnet 复制代码
const addTodo = (text) => {
  if (text.trim() === '') return 
  setTodos([
    {id: +Date.now(), text, completed: false },
    ...todos
  ])
}

流程

  1. TodoInput(输入框子组件,孩子) 用户输入文字,按下回车
  2. 孩子没有权限直接改清单,于是调用爸爸给的 onAdd,把输入文字传给爸爸
  3. 进入 addTodo
  • if (text.trim() === '') return:如果输入是空的,直接不处理,禁止空白任务
  • +Date.now():拿当前时间戳当做新任务独一无二的编号
  • [新任务, ...todos]:造一张全新清单,新任务放最上面,旧任务原样带上
  • setTodos(新清单):爸爸替换手里的旧清单 → 页面自动刷新

...todos 大白话:把旧清单里所有任务全部复制出来。

3. 函数 2:toggleTodo 勾选 / 取消任务

jsx

ini 复制代码
const toggleTodo = (id) => {
  setTodos(todos.map(todo => 
    todo.id === id 
      ? { ...todo, completed: !todo.completed}
      : todo
  ))
}

流程

  1. 用户点击任务前面的复选框(子组件 TodoList)
  2. 子组件把这条任务的 id 传给爸爸提供的 onToggle
  3. map 循环旧清单,生成一张全新清单
  • 如果当前循环这条任务 id 和点击的 id 一致:复制这条任务 {...todo},把完成状态取反 !todo.completed
  • 不匹配:直接原样保留这条任务
  1. setTodos 替换清单,页面刷新

❌ 禁止直接写 todo.completed = !todo.completed。不能在原对象上修改!

4. 函数 3:deleteTodo 删除任务

jsx

ini 复制代码
const deleteTodo = (id) => {
  setTodos(todos.filter(todo => todo.id !== id))
}

流程

  1. 用户点击某条任务的删除按钮(子组件 TodoList)
  2. 子组件把要删除任务的 id 传给爸爸 onDelete
  3. filter 筛选:只保留 id 不等于要删除 id 的任务,自动剔除目标任务
  4. 返回筛选后的全新清单,setTodos 更新页面

5. 组件调用部分(父子沟通核心)

jsx

ini 复制代码
<TodoInput onAdd={addTodo}/>

<TodoList 
  todos={todos} 
  onToggle={toggleTodo}
  onDelete={deleteTodo}
/>

<TodoStats todos={todos}/>

形象比喻:

  • <TodoInput> 输入框孩子:爸爸给它一个工具 onAdd。孩子输入内容后,喊爸爸:"帮我新增一条任务!"
  • <TodoList> 列表孩子:爸爸把清单todos复印件交给它展示。同时给它两个工具:onToggle(切换完成)、onDelete(删除)。孩子点击勾选 / 删除,只能通知爸爸干活,自己不能修改清单原件。
  • <TodoStats> 统计孩子:只拿清单复印件,单纯展示已完成多少条,不需要修改,所以不给工具函数。

核心思想一句话总结所有原始数据清单,只保存在父组件 App。子组件只负责展示页面。子想要修改数据,只能调用父提供的函数,由父组件统一修改数据。

完整数据流例子(新增一条任务)

  1. 用户在输入框打字:学习React
  2. 回车 → TodoInput 执行 onAdd("学习React")
  3. 父组件 addTodo 执行,生成新任务,构建全新任务清单
  4. setTodos 更新状态
  5. App 组件刷新,TodoList、TodoStats 拿到新清单,页面自动更新

补充:配套子组件完整代码(可直接运行)

TodoInput.jsx

jsx

javascript 复制代码
import { useState } from 'react'
export default function TodoInput({onAdd}){
  const [value, setValue] = useState('')
  const handleKeyDown = (e)=>{
    if(e.key === 'Enter'){
      onAdd(value)
      setValue('')
    }
  }
  return (
    <input 
      value={value}
      onChange={(e)=>setValue(e.target.value)}
      onKeyDown={handleKeyDown}
      placeholder="请输入待办事项,回车添加"
    />
  )
}

TodoList.jsx

jsx

javascript 复制代码
export default function TodoList({todos,onToggle,onDelete}){
  return (
    <ul>
      {todos.map(item=>(
        <li key={item.id}>
          <input 
            type="checkbox" 
            checked={item.completed}
            onChange={()=>onToggle(item.id)}
          />
          <span style={{textDecoration:item.completed?'line-through':'none'}}>
            {item.text}
          </span>
          <button onClick={()=>onDelete(item.id)}>删除</button>
        </li>
      ))}
    </ul>
  )
}

TodoStats.jsx

jsx

javascript 复制代码
export default function TodoStats({todos}){
  const total = todos.length
  const finished = todos.filter(i=>i.completed).length
  return <div>总共:{total}条,已完成:{finished}条</div>
}

小结

TodoList 看着简单,但是包含 React 最核心的单向数据流、状态提升、不可变数据思想。记住:数据向上集中,交互向下传递回调。所有子组件不要尝试直接修改 props 数据,通知父组件处理状态更新,是标准写法。

相关推荐
kisshyshy3 小时前
《端侧大模型(三)“精装修”日记:进度条整容、聊天框首秀》
react.js·typescript·代码规范
lymboy5 小时前
Spring AI的工具调用循环,本质就是ReAct:一次源码级拆解
人工智能·spring·react.js
Darling噜啦啦18 小时前
React 组件化实战:从 TodoList 吃透父子通信与状态管理
react.js
bloglin999991 天前
langchain 和 langgraph 和 react
javascript·react.js·langchain
触底反弹1 天前
🚀 删了数据刷新又回来?3 组件 × 4 回调 × 3 坑讲透 React 父子通信
前端·javascript·react.js
sugar__salt1 天前
useState 深入浅出:React 状态管理的基石
前端·react.js·前端框架
moMo1 天前
状态管理第一步:React useState 核心行为完全拆解
react.js
拾年2751 天前
React 组件通信完全指南 —— 从 Todo App 搞懂父子组件那点事
react.js
先吃饱再说1 天前
React 组件通信:从 Props 单向传递到自定义事件
前端·react.js·前端框架