前言
在现代前端开发中,React 已成为构建用户界面的主流框架之一。通过组件化和状态管理,开发者可以轻松创建高效、可维护的应用程序。本文将以一个完整的 Todo 应用 为例,详细介绍如何使用 React 实现父子组件通信、状态管理以及事件处理等核心功能。我们将从项目结构入手,逐步解析每个组件的功能,并提供一些补充知识帮助你更好地理解。
一、项目概述
1.1 功能需求
- 添加待办事项:用户可以通过输入框添加新的待办事项。
- 删除待办事项:用户可以删除已有的待办事项。
- 标记完成:用户可以通过勾选复选框标记待办事项为已完成或未完成。
- 清除已完成项:用户可以一键清除所有已完成的待办事项。
1.2 技术栈
- React: 主要框架,用于构建用户界面。
- Stylus: CSS 预处理器,简化样式编写。
- Vite: 构建工具,支持快速开发和热更新。
二、项目结构
src/
├── components/
│ ├── TodoInput.jsx // 输入框组件
│ ├── TodoList.jsx // 待办列表组件
│ └── TodoStats.jsx // 状态统计组件
├── styles/
│ └── app.styl // 全局样式
└── App.jsx // 根组件
三、组件详解
3.1 TodoInput 组件
功能描述
- 提供一个输入框,允许用户输入待办事项。
- 当用户点击“ADD”按钮时,触发
onAdd回调函数,将输入的内容传递给父组件。
代码实现
import { useState } from 'react';
const TodoInput = (props) => {
const { onAdd } = props;
const [inputValue, setInputValue] = useState('');
const handleSubmit = (e) => {
e.preventDefault();
if (inputValue.trim() === '') return; // 可选:防止空内容
onAdd(inputValue);
setInputValue(''); // 清空输入框
};
return (
<form className="todo-input" onSubmit={handleSubmit}>
<input
type="text"
value={inputValue}
onChange={(e) => setInputValue(e.target.value)}
placeholder="输入待办事项..."
/>
<button type="submit">ADD</button>
</form>
);
};
export default TodoInput;
关键点解析
useState: 用于管理输入框的状态。handleSubmit: 表单提交时阻止默认行为,并调用onAdd回调函数传递输入值。
3.2 TodoList 组件
功能描述
- 展示所有待办事项的列表。
- 每个待办事项包含一个复选框(用于标记完成/未完成)和一个删除按钮。
代码实现
const TodoList = (props) => {
const { todos, onDelete, onToggle } = props;
return (
<ul className="todo-list">
{todos.length === 0 ? (
<li className="empty">No todos yet!</li>
) : (
todos.map((todo) => (
<li key={todo.id} className={todo.completed ? 'completed' : ''}>
<label>
<input
type="checkbox"
checked={todo.completed}
onChange={() => onToggle(todo.id)}
/>
<span>
{todo.text}
</span>
</label>
<button onClick={() => onDelete(todo.id)}>x</button>
</li>
))
)}
</ul>
);
};
export default TodoList;
关键点解析
map: 遍历todos数组,生成对应的<li>元素。onDelete和onToggle: 分别用于删除和切换待办事项的状态。
3.3 TodoStats 组件
功能描述
- 显示待办事项的总数、活跃数和已完成数。
- 提供一个按钮用于清除所有已完成的待办事项。
代码实现
const TodoStats = (props) => {
const {
total,
active,
completed,
onClearCompleted
} = props;
return (
<div className="todo-stats">
<p>Total: {total} | Active: {active} | Completed: {completed}</p>
{
completed > 0 && (
<button
onClick={onClearCompleted}
className="clear-btn"
>
Clear Completed
</button>
)
}
</div>
);
};
export default TodoStats;
关键点解析
- 条件渲染: 当
completed大于 0 时,才显示“Clear Completed”按钮。
3.4 App 组件
功能描述
- 作为应用的根组件,负责管理全局状态并协调子组件之间的交互。
代码实现
import { useState, useEffect } from 'react';
import TodoInput from './components/TodoInput.jsx';
import TodoList from './components/TodoList.jsx';
import TodoStats from './components/TodoStats.jsx';
import './styles/app.styl';
function App() {
// 初始化状态
const [todos, setTodos] = useState(() => {
const saved = localStorage.getItem('todos');
return saved ? JSON.parse(saved) : [];
});
// 同步到本地存储
useEffect(() => {
localStorage.setItem('todos', JSON.stringify(todos));
}, [todos]);
// 添加待办事项
const addTodos = (text) => {
if (!text?.trim()) return;
setTodos(prev => [...prev, {
id: Date.now(),
text: text.trim(),
completed: false
}]);
};
// 删除待办事项
const deleteTodo = (id) => {
setTodos(todos.filter(todo => todo.id !== id));
};
// 切换待办事项状态
const toggleTodo = (id) => {
setTodos(todos.map(todo => todo.id === id ? {
...todo,
completed: !todo.completed,
} : todo));
};
// 清除已完成的待办事项
const clearCompleted = () => {
setTodos(todos.filter(todo => !todo.completed));
};
// 计算活跃和已完成数量
const activeCount = todos.filter(todo => !todo.completed).length;
const completedCount = todos.filter(todo => todo.completed).length;
return (
<div className="todo-app">
<h1>My Todo List</h1>
<TodoInput onAdd={addTodos} />
<TodoList todos={todos} onDelete={deleteTodo} onToggle={toggleTodo} />
<TodoStats
total={todos.length}
active={activeCount}
completed={completedCount}
onClearCompleted={clearCompleted}
/>
</div>
);
}
export default App;
关键点解析
useState: 用于管理待办事项列表的状态。useEffect: 在每次todos发生变化时,同步到本地存储。- 事件处理函数:
addTodos,deleteTodo,toggleTodo,clearCompleted分别处理不同的操作。
四、父子组件通信
4.1 数据传递
- 父组件
App将数据 (todos) 和操作方法 (onAdd,onDelete,onToggle,onClearCompleted) 通过props传递给子组件。 - 子组件通过回调函数的方式通知父组件进行状态更新。
4.2 示例
TodoInput通过onAdd回调将新任务传递给父组件。TodoList通过onDelete和onToggle回调分别通知父组件删除和切换任务状态。TodoStats通过onClearCompleted回调通知父组件清除已完成的任务。
五、性能优化与最佳实践
5.1 使用 localStorage
- 通过
useEffect钩子,在每次todos发生变化时自动保存到本地存储,确保页面刷新后数据不会丢失。
5.2 条件渲染
- 在
TodoStats中,只有当存在已完成任务时才渲染“Clear Completed”按钮,避免不必要的 DOM 渲染。
六、总结
通过这个简单的 Todo 应用,我们深入探讨了 React 的核心概念,包括:
- 组件化开发:将复杂的应用拆分为多个独立的组件,便于维护和扩展。
- 状态管理:使用
useState和useEffect管理组件内部状态,并与其他组件共享。 - 父子组件通信:通过
props传递数据和回调函数,实现组件间的双向通信。
希望这篇文章能帮助你更好地理解 React 的工作原理,并为你未来的项目提供有价值的参考。如果你有任何问题或建议,请随时留言讨论!