构建 React Todo 应用:组件通信与状态管理的最佳实践

Todo 应用虽小,却是检验框架理解深度的"试金石"。它涵盖了状态管理、组件拆分、数据流控制、本地持久化等核心概念。使用 React 构建这样一个应用,不仅能体现函数式编程与组件化思想的优势,更能清晰展示父子组件通信、状态提升与单向数据流的设计哲学。

状态集中管理:单一数据源原则

React 推崇"状态提升"------将共享状态置于最近的共同父组件中。在 Todo 应用中,所有待办事项 todos 被统一管理在 App 组件内:

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

通过 useState 的函数式初始化,应用启动时自动从 localStorage 恢复历史数据。这种设计确保了整个应用只有一个"真相来源",避免了状态分散导致的不一致问题。

子组件通信:通过 Props 传递数据与行为

子组件如 TodoInputTodoListTodoStats 均不持有自身状态,而是通过 props 接收数据和操作方法:

ini 复制代码
<TodoInput onAdd={addTodo} />
<TodoList 
  todos={todos} 
  onDelete={deleteTodo}
  onToggle={toggleTodo}
/>
<TodoStats 
  total={todos.length}
  active={activeCount}
  completed={completedCount}
  onClearCompleted={clearCompleted}
/>
  • TodoInput 接收 onAdd 回调,用于提交新任务;
  • TodoList 接收任务列表及删除、切换完成状态的方法;
  • TodoStats 显示统计信息并提供清除已完成项的功能。

这种模式严格遵循单向数据流 :数据自上而下传递,修改请求自下而上传递。子组件无法直接修改 todos,只能调用父组件提供的函数发起变更,确保状态演进的可预测性。

子到父通信:回调函数作为事件通道

React 不支持子组件直接修改父状态,但可通过回调函数实现"事件上报"。例如,TodoInput 在表单提交时调用 onAdd

ini 复制代码
const handleSubmit = (e) => {
  e.preventDefault();
  onAdd(inputValue);
  setInputValue('');
};

onAdd 实际是 App 中定义的 addTodo 函数,它接收文本并生成新任务:

ini 复制代码
const addTodo = (text) => {
  setTodos([...todos, {
    id: Date.now(),
    text,
    completed: false
  }]);
};

类似地,TodoList 中的复选框和删除按钮分别触发 onToggleonDelete,将用户操作转化为对中心状态的更新请求。这种机制解耦了 UI 与逻辑,使组件更专注于自身职责。

兄弟组件通信:间接通过父组件协调

TodoInputTodoListTodoStats 是兄弟关系,彼此无直接引用。它们的数据同步完全依赖父组件 App 的协调:

  • TodoInput 添加新任务,App 更新 todos
  • todos 作为 props 传递给 TodoListTodoStats
  • 二者自动重新渲染,显示最新内容。

这种"间接通信"看似绕路,实则保证了数据流的清晰与可追踪。任何状态变化都源于明确的父级更新,极大降低了调试复杂度。

本地持久化:副作用中的数据同步

为防止页面刷新丢失数据,应用需将 todos 同步至 localStorage。这属于典型的副作用操作,由 useEffect 处理:

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

每当 todos 发生变化(依赖项 [todos] 触发),该副作用自动执行,将最新状态写入存储。这种声明式写法将持久化逻辑与业务逻辑分离,保持主渲染路径的纯净。

样式与用户体验:Stylus 与条件渲染

项目采用 Stylus 预处理器编写样式,配合 React 的条件类名实现动态 UI:

ini 复制代码
<li className={todo.completed ? 'completed' : ''}>

当任务完成时,添加 completed 类,应用删除线等视觉反馈。同时,TodoStats 仅在存在已完成项时显示"清除"按钮:

css 复制代码
{completed > 0 && <button onClick={onClearCompleted}>Clear Completed</button>}

这种细粒度的控制提升了界面的响应性与友好度。

总结

这个 Todo 应用虽功能简单,却完整体现了 React 的核心思想:组件化、单向数据流、状态集中管理与副作用隔离 。通过将状态置于顶层、通过 Props 传递数据与回调、利用 useEffect 处理持久化,代码结构清晰、逻辑内聚、易于扩展。无论是初学者理解 React 原理,还是团队建立开发规范,此类实践都提供了坚实的基础。掌握这些模式,便能在更复杂的业务场景中游刃有余地构建健壮、可维护的前端应用。

相关推荐
nuIl21 小时前
我把 5 个编码 Agent 塞进了一个 npm 包
前端·agent·claude
L-影1 天前
FastAPI 静态文件:Web 页面的“固定展柜”与“加速引擎”
前端·fastapi
陪我去看海1 天前
受够了 AI 写完代码留下一堆端口?我做了一个端口管理App!
前端·macos·vibecoding
Xuepoo1 天前
我抛弃了 DOM,但保留了无障碍访问
前端
李明卫杭州1 天前
Vue2 vs Vue3 的 h 函数:渲染函数完整迁移指南
前端
星栈1 天前
LiveView 的认证系统:从登录到权限,我一开始以为有 `current_user` 就算完事了
前端·前端框架·elixir
在因斯坦1 天前
摸摸鱼之前端自己研究 Jenkins 自动化部署
前端
snpgroupcn1 天前
SAP实施风险管控注意事项:四大风险域与防范策略
前端·数据库·人工智能
阿成学长_Cain1 天前
Linux talk 命令详解:经典终端实时聊天工具,无需图形界面即可对话
linux·运维·前端
lichenyang4531 天前
从零开始写 HarmonyOS Web 容器:先让 ArkWeb 加载本地 H5
前端