🎯 从零搭建一个 React Todo 应用:父子通信、状态管理与本地持久化全解析!

"写 Todo 是程序员的成人礼。"

如果你刚刚入坑 React,或者想巩固组件通信、状态提升、本地存储等核心概念,那么恭喜你!这篇文章将带你手把手打造一个功能完整、结构清晰、代码优雅的 React Todo 应用,并深入浅出地解释背后的原理。

更重要的是------我们不用 Redux、不用 Context、不用任何花里胡哨的库,只用 React 原生 Hooks + 父子通信,就能写出可维护、可扩展的代码!


🧠 为什么 Todo 应用值得认真对待?

别小看这个"加任务、删任务、标记完成"的小玩意儿。它完美涵盖了现代前端开发的三大核心问题:

  1. 状态管理(谁持有数据?谁修改数据?)
  2. 组件通信(父子怎么传?兄弟怎么聊?)
  3. 副作用处理(比如自动保存到 localStorage)

而 React 的哲学是:状态提升 + 单向数据流 。听起来高大上?其实很简单------让父组件当"管家",子组件只负责"汇报"和"展示" 。


🏗️ 项目结构预览

我们的应用由三个子组件构成:

  • TodoInput:输入新任务
  • TodoList:展示并操作任务列表
  • TodoStats:显示统计信息 & 清除已完成

它们都共享同一个状态:todos[]。这个数组由父组件 App 统一管理 ,并通过 props 传递给子组件。

✨ 这就是"状态提升"(Lifting State Up)的经典实践!


🔌 父子通信:React 的"单向数据流"哲学

👨‍👧 父 → 子:通过 props 传递数据

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

父组件把 todos 数组和几个修改函数作为 props 传给子组件。子组件只能读,不能改------就像孩子只能看菜单,不能自己进厨房炒菜。

👧‍→👨 子 → 父:通过回调函数"打报告"

子组件想修改数据?必须调用父组件传来的函数:

scss 复制代码
// 在 TodoInput 中
onAdd(inputValue); // 相当于:"爸,我想加个任务!"

// 在 TodoList 中
onToggle(todo.id); // "爸,这个任务我搞定了!"

这种模式确保了数据流向清晰、可预测,避免了"状态混乱"的噩梦。

💡 小贴士:React 不支持 Vue 那样的 v-model 双向绑定,因为它认为"显式优于隐式"。虽然多写两行代码,但逻辑更透明!


🧩 兄弟组件如何"隔空对话"?

TodoInput 和 TodoList 是兄弟,它们之间没有直接通信 !所有交互都通过共同的父组件 App 中转:

  1. TodoInput 调用 onAdd → 父组件更新 todos
  2. 父组件把新 todos 传给 TodoList → 列表自动刷新

这就是所谓的 "间接通信" ------看似绕路,实则解耦。兄弟组件互不依赖,未来拆分或替换都超轻松!


💾 自动保存到 localStorage:useEffect 的妙用

用户辛辛苦苦加了一堆任务,结果一刷新全没了?那可不行!

我们用 useEffect 监听 todos 变化,自动存到本地:

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

同时,初始化时从 localStorage 读取:

ini 复制代码
const [todos, setTodos] = useState(() => {
  const saved = localStorage.getItem('todos');
  return saved ? JSON.parse(saved) : [];
});

🎉 用户体验瞬间拉满:关掉浏览器再打开,任务还在!妈妈再也不用担心我丢三落四了~


🎨 样式方案:Stylus + Vite,简洁又高效

我们用 Stylus 写样式(缩进语法,少写大括号),配合 Vite 极速构建。.styl 文件清爽易读:

yaml 复制代码
.todo-app
  max-width: 600px
  margin: 0 auto
  padding: 20px
  
  .completed
    text-decoration: line-through
    color: #888

Vite 的 HMR(热更新)快如闪电,改一行样式,浏览器秒级响应------开发幸福感爆棚!


🧪 完整代码亮点回顾

  • ✅ 状态集中管理 :所有 todos 操作在 App 中定义
  • ✅ 函数式更新 :setTodos([...todos, newTodo]) 避免闭包陷阱
  • ✅ 条件渲染 :completed > 0 && <button> 避免无效按钮
  • ✅ 语义化 JSX :<label> 包裹 checkbox,提升可访问性
  • ✅ 性能友好:无多余状态,无复杂计算

🤔 思考:为什么不用 Context 或 Zustand?

对于小型应用(如 Todo),过度设计反而增加复杂度 。Context 适合跨多层组件共享状态,Zustand 适合大型状态树。而我们的场景------三个组件 + 一个状态数组,用 props 足矣!

🚀 记住:简单即强大。能用 props 解决的问题,就别急着上状态管理库!


🎁 结语:你的第一个 React 应用,也可以很优雅

通过这个 Todo 应用,你不仅学会了组件通信,更理解了 React 的核心思想:状态驱动视图、单向数据流、组合优于继承。

下次面试官问:"React 组件怎么通信?" 你可以微微一笑,掏出这个项目说:

"看,我的 Todo,麻雀虽小,五脏俱全。"

相关推荐
mftang3 分钟前
CANopen协议:基于CAN的高层协议架构、通信模型与工业应用深度解析
架构·canopen·对象字典·canopen fd
可乐鸡翅yeah_35 分钟前
hls.js 手动自定义 http 请求 loader,修改请求头实战
开发语言·前端·javascript·网络协议·http·ecmascript·m3u8在线
IT_陈寒1 小时前
Vite静态资源导入这个坑我帮你们踩过了
前端·人工智能·后端
广州华水科技1 小时前
大坝安全监测解决方案:单北斗GNSS形变监测系统应用与维护
前端
yivifu2 小时前
中文古籍电子书注释集成
前端·javascript·python·beautifulsoup·epub
默_笙2 小时前
🛴 从散件到整机:DeepAgents 与 Agent 身上预留的那些"插槽"(前置介绍)
前端·javascript
zhangzeyuaaa3 小时前
深入理解 Ruby 运算符:本质、分类、坑点与重载实战
开发语言·前端·ruby
一木 之林3 小时前
DeepSeek Agent 开发(一)
开发语言·前端·javascript
树下水月4 小时前
Typora破解
linux·服务器·前端
Learn_PLC4 小时前
自动化工程师Demand与工业机器人技术发展的未来趋势分析
架构