从 Todo 项目看 React 组件通信:核心逻辑与优化技巧

React 真正让人头疼的,从来不是 Hooks 的 API,而是:组件一多,数据到底该往哪放?

很多人写 React,都是典型的「能跑就行」👇

  • Props 层层传递(Prop Drilling),改一个逻辑要动五个文件
  • 兄弟组件状态不同步,Bug 像"量子纠缠"
  • Debug 像破案,根本不知道是谁把 todos 改了

问题不在语法,在"通信模型"没设计清楚。

今天,我们就通过一个经典的 Todo List 项目 ,把 React 组件通信的底层逻辑一次性讲透


一、架构先行:先站在"上帝视角"看组件树

在 React 里,通信问题 80% 不是代码问题,而是结构问题。

1️⃣ 组件分工一览

arduino 复制代码
App
├── TodoInput   // 动作发起者
├── TodoList    // 状态展示者
└── TodoStats   // 数据统计者
  • App(数据中心)
    唯一的 Source of Truth ,持有 todos
  • TodoInput(动作发起者)
    只负责收集输入,不关心数据存哪
  • TodoList(展示者)
    渲染列表,并触发"删除 / 切换完成态"
  • TodoStats(统计者)
    根据数据计算统计信息,并提供"清空"能力

2️⃣ 核心原则:状态提升(Lifting State Up)

只要多个组件共享同一份数据,这份数据就必须上移。

为什么 todos 不能放在 TodoList

因为:

  • TodoInput 要新增
  • TodoStats 要统计
  • TodoList 要展示

👉 三者共享 ⇒ 数据只能放在最近的共同父组件 App

这不是最佳实践,这是 React 的设计前提


二、三大通信场景深度拆解


1️⃣ 父 → 子:Props 不是"传数据",而是"授予使用权"

App.jsx 中:

ini 复制代码
<TodoList
  todos={todos}
  onDelete={deleteTodo}
  onToggle={toggleTodo}
/>

💡 深度理解 Props:

Props 不是拷贝一份数据

而是父组件给子组件的「只读使用权」

📌 类比一下:

  • 父组件是老板
  • Props 是任务清单
  • 子组件只能照单执行,不能私自改单

子组件永远不应该修改 Props。


2️⃣ 子 → 父:从"改数据"到"上报事件"

这是 React 新手最容易犯错的地方。

❌ 错误思维:

"我能不能在子组件里直接 props.todos.push()?"

✅ 正确模型:

子组件只负责"上报行为",不负责"决定结果"


TodoInput 中:

ini 复制代码
const TodoInput = ({ onAdd }) => {
  const [text, setText] = useState('');

  const handleSubmit = () => {
    if (!text.trim()) return;
    onAdd(text); // 📢 向父组件上报:新增了一个任务
    setText('');
  };
};

❓ 为什么一定要用函数?

因为:

  • 只有 App 持有 setTodos
  • 只有 App 有资格决定数据如何变化
  • Bug 出现时,你只需要检查 一个地方

把"修改权"集中,是 React 可维护性的核心来源。


3️⃣ 兄弟组件通信:最熟悉的陌生人

TodoStats 如何"知道" TodoList 里发生了变化?

答案是:它并不知道。


正确流程是这样的:

1️⃣ TodoList 触发 onToggle(子 → 父)

2️⃣ App 更新 todos

3️⃣ App 重新渲染

4️⃣ 新的 todos 通过 Props 下发给 TodoStats

💡 金句记住这一句:

在 React 中,兄弟组件之间永远"互不认识",它们只对父组件负责。


三、高阶技巧:避开通信中的"隐形坑"


1️⃣ 别把 Props 当 State:警惕派生状态

❌ 常见错误:

scss 复制代码
const [activeCount, setActiveCount] = useState(0);

useEffect(() => {
  setActiveCount(todos.filter(t => !t.completed).length);
}, [todos]);

✅ 正确做法:

ini 复制代码
const activeCount = todos.filter(todo => !todo.completed).length;

📌 原则只有一句话:

如果一个值可以通过 Props 计算得出,就永远不要用 useState 存。

这类数据叫:派生状态(Derived State)


2️⃣ 只有"副作用"才进 useEffect

在 Todo 项目中,真正需要 useEffect 的只有一件事👇

javascript 复制代码
useEffect(() => {
  localStorage.setItem('todos', JSON.stringify(todos));
}, [todos]);

✅ 特点:

  • 不影响渲染
  • 依赖明确
  • 职责单一

👉 这才是 useEffect 的正确姿势。


四、总结:React 通信的"秩序感"

React 坚持单向数据流,不是为了限制你,而是为了建立秩序

📌 记住这 4 句话:

  • 数据向上找:共享就提升
  • 展示向下走:子组件只看 Props
  • 修改调函数:子组件动口不动手
  • 兄弟不直连:父组件来转达

🔥 最后送你 5 句开发口诀

✅ 状态放对位,代码省一半

✅ Props 是合同,子组件得照办

✅ 改数据发请求,别在子组件里乱走

✅ 兄弟不传话,老爸来转达

✅ 算出来的状态,别用 State 存


🎯 结语

Todo 项目很小,

但它几乎覆盖了 React 组件通信的所有核心场景

如果你真正吃透这一套模型:

  • Redux / Zustand 会立刻"通电"
  • 项目越大,你越稳
  • 面试官问组件通信,你能讲设计而不是 API
相关推荐
康一夏4 小时前
React面试题,封装useEffect
前端·javascript·react.js
社恐的下水道蟑螂6 小时前
深入掌握 AI 全栈项目中的路由功能:从基础到进阶的全面解析
前端·react.js·全栈
FanetheDivine6 小时前
图片标注框选组件
前端·react.js
子玖6 小时前
antd6的table排序功能
前端·react.js
徐同保6 小时前
react两个组件中间加一个可以拖动跳转左右大小的功能
前端·javascript·react.js
2501_948194988 小时前
RN for OpenHarmony AnimeHub项目实战:正在热播页面开发
javascript·react native·react.js
2501_944521008 小时前
rn_for_openharmony商城项目app实战-语言设置实现
javascript·数据库·react native·react.js·harmonyos
溪海莘9 小时前
React入门:跟读官方快速入门教程(前端小白)
前端·react.js·前端框架
cauyyl9 小时前
react 项目检查国际化配置脚本
前端·react.js·前端框架
康一夏9 小时前
React面试题,useRef和普通变量的区别
前端·javascript·react.js