🎯 从零搭建一个 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 双向绑定,因为它认为"显式优于隐式"。虽然多写两行代码,但逻辑更透明!


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

TodoInputTodoList 是兄弟,它们之间没有直接通信 !所有交互都通过共同的父组件 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,麻雀虽小,五脏俱全。"

相关推荐
m0_7190841128 分钟前
React笔记张天禹
前端·笔记·react.js
Ziky学习记录42 分钟前
从零到实战:React Router 学习与总结
前端·学习·react.js
wuhen_n1 小时前
JavaScript链表与双向链表实现:理解数组与链表的差异
前端·javascript
wuhen_n1 小时前
JavaScript数据结构深度解析:栈、队列与树的实现与应用
前端·javascript
狗哥哥1 小时前
微前端路由设计方案 & 子应用管理保活
前端·架构
青青家的小灰灰1 小时前
React 19 核心特性与版本优化深度解析
react.js
Max_uuc1 小时前
【架构心法】对抗熵增:嵌入式系统中的“数据完整性”保卫战
架构
前端大卫2 小时前
Vue3 + Element-Plus 自定义虚拟表格滚动实现方案【附源码】
前端
却尘2 小时前
Next.js 请求最佳实践 - vercel 2026一月发布指南
前端·react.js·next.js
ccnocare2 小时前
浅浅看一下设计模式
前端