🎯 从零搭建一个 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,麻雀虽小,五脏俱全。"

相关推荐
用户4099322502122 小时前
Vue3 v-if与v-show:销毁还是隐藏,如何抉择?
前端·vue.js·后端
Mr_chiu2 小时前
🚀 效率暴增!Vue.js开发必知的15个神级提效工具
前端
shanLion2 小时前
Vite项目中process报红问题的深层原因与解决方案
前端·javascript
黄俊懿2 小时前
【深入理解SpringCloud微服务】Seata(AT模式)源码解析——全局事务的回滚
java·后端·spring·spring cloud·微服务·架构·架构师
烟袅2 小时前
从零构建一个待办事项应用:一次关于组件化与状态管理的深度思考
前端·javascript·react.js
前端小万2 小时前
草稿
前端
闲云一鹤2 小时前
将地图上的 poi 点位导出为 excel,并转换为 shp 文件
前端·cesium
柏木乃一2 小时前
进程(6)进程切换,Linux中的进程组织,Linux进程调度算法
linux·服务器·c++·算法·架构·操作系统
岁月宁静3 小时前
MasterGo AI 实战教程:10分钟生成网页设计图(附案例演示)
前端·aigc·视觉设计