React 入门到实战:组件、状态与 Hooks
React 入门到实战:组件、状态与 Hooks
1. 认识 React
React 是由 Facebook 开发的用于构建用户界面的 JavaScript 库。它的核心理念是 组件化:将 UI 拆分为独立、可复用的模块,每个模块管理自己的状态和渲染逻辑。React 使用 声明式 写法,只需描述界面应该长什么样,React 负责高效更新 DOM。
现代 React 开发以 函数组件 + Hooks 为主,我们将围绕这一范式进行学习。
2. 开发环境准备
推荐使用 Vite 快速初始化项目。在终端中运行:
npm create vite@latest react-tutorial -- --template react
cd react-tutorial
npm install
npm run dev
浏览器访问 http://localhost:5173 即可看到默认页面。
3. 第一个函数组件与 JSX
3.1 JSX 语法
JSX 是 JavaScript 的语法扩展,看起来像 HTML,但最终会被编译为 React.createElement 调用。它允许你在 JavaScript 中直接书写 UI 结构。
function Welcome() {
return <h1>Hello, React!</h1>;
}
3.2 JSX 规则速览
- 返回单个根元素:必须用
<div>或空标签<>...</>包裹多个元素。 - 使用
{}嵌入表达式:可以插入变量、函数调用等。 - 属性命名:
class写成className,for写成htmlFor。 - 自闭合标签必须闭合:
<br />、<input />。
3.3 组件导出与使用
组件文件通常使用默认导出:
// Welcome.jsx
export default function Welcome({ name }) {
return <h2>欢迎,{name}!</h2>;
}
在其他组件中导入并使用:
import Welcome from './Welcome';
function App() {
return (
<div>
<Welcome name="小明" />
<Welcome name="小红" />
</div>
);
}
4. Props:父子组件通信
Props 是父组件传递给子组件的只读数据。在函数组件中通过第一个参数接收。
function UserCard({ avatar, name, bio }) {
return (
<div className="card">
<img src={avatar} alt={name} />
<h3>{name}</h3>
<p>{bio}</p>
</div>
);
}
传递 Props 时,可以传递字符串、数字、对象、函数等任意 JavaScript 类型。如果 Prop 没有被传递,可以设置默认值:
function UserCard({ name = '匿名用户' }) { ... }
5. 状态管理:useState Hook
Hook 是 React 16.8 引入的特性,让你在函数组件中使用 state 和其他 React 功能。useState 是最基础的 Hook。
5.1 声明状态
import { useState } from 'react';
function Counter() {
// count 是当前状态值,setCount 是更新函数
const [count, setCount] = useState(0);
return (
<div>
<p>当前计数:{count}</p>
<button onClick={() => setCount(count + 1)}>+1</button>
</div>
);
}
useState(初始值)返回一个数组,使用解构赋值命名。- 状态更新后,组件会重新渲染。
- 状态是异步更新的:如果需要根据前一个状态计算新值,应该使用函数式更新:
setCount(prev => prev + 1)。
5.2 对象或数组状态
当状态是对象或数组时,必须 替换而不是修改 原值,以保持不可变性。
const [user, setUser] = useState({ name: '', age: 0 });
// 正确更新方式
setUser({ ...user, name: '张三' });
// 数组同理
const [list, setList] = useState([]);
setList([...list, newItem]);
6. 处理副作用:useEffect Hook
useEffect 让你在组件渲染后执行副作用操作(如数据获取、订阅、手动修改 DOM 等)。
6.1 基本用法
import { useState, useEffect } from 'react';
function Timer() {
const [seconds, setSeconds] = useState(0);
useEffect(() => {
const interval = setInterval(() => {
setSeconds(s => s + 1);
}, 1000);
// 清理函数:组件卸载或依赖变化时执行
return () => clearInterval(interval);
}, []); // 空依赖数组 → 只在挂载时运行一次
return <div>运行时间:{seconds} 秒</div>;
}
- 第一个参数是回调函数,在 DOM 更新后执行。
- 第二个参数是 依赖数组:
- 若不传,每次渲染后都执行(少用)。
- 空数组
[],仅在首次渲染(挂载)时执行一次。 - 传入特定值
[count],仅在count变化时执行。
6.2 常见场景:数据请求
function UserList() {
const [users, setUsers] = useState([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
async function fetchUsers() {
try {
const res = await fetch('https://jsonplaceholder.typicode.com/users');
const data = await res.json();
setUsers(data);
} catch (error) {
console.error('请求失败', error);
} finally {
setLoading(false);
}
}
fetchUsers();
}, []);
if (loading) return <p>加载中...</p>;
return (
<ul>
{users.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
}
7. 实战项目:任务待办清单
我们将综合运用组件、Props、useState 和 useEffect 构建一个简单的 Todo 应用。
7.1 应用结构
- App:主组件,管理任务列表状态。
- AddTodo:输入框与添加按钮。
- TodoList:展示任务列表。
- TodoItem:单个任务,支持完成状态切换和删除。
7.2 完整代码
// App.jsx
import { useState } from 'react';
import AddTodo from './AddTodo';
import TodoList from './TodoList';
function App() {
const [todos, setTodos] = useState([]);
const addTodo = (text) => {
const newTodo = {
id: Date.now(),
text,
completed: false,
};
setTodos([...todos, newTodo]);
};
const toggleTodo = (id) => {
setTodos(todos.map(todo =>
todo.id === id ? { ...todo, completed: !todo.completed } : todo
));
};
const deleteTodo = (id) => {
setTodos(todos.filter(todo => todo.id !== id));
};
return (
<div style={{ maxWidth: 400, margin: '0 auto' }}>
<h1>待办事项</h1>
<AddTodo onAdd={addTodo} />
<TodoList
todos={todos}
onToggle={toggleTodo}
onDelete={deleteTodo}
/>
</div>
);
}
export default App;
// AddTodo.jsx
import { useState } from 'react';
export default function AddTodo({ onAdd }) {
const [text, setText] = useState('');
const handleSubmit = (e) => {
e.preventDefault();
if (text.trim()) {
onAdd(text.trim());
setText('');
}
};
return (
<form onSubmit={handleSubmit} style={{ marginBottom: 16 }}>
<input
type="text"
value={text}
onChange={(e) => setText(e.target.value)}
placeholder="输入新任务..."
/>
<button type="submit">添加</button>
</form>
);
}
// TodoList.jsx
import TodoItem from './TodoItem';
export default function TodoList({ todos, onToggle, onDelete }) {
if (todos.length === 0) return <p>暂无任务</p>;
return (
<ul>
{todos.map(todo => (
<TodoItem
key={todo.id}
todo={todo}
onToggle={onToggle}
onDelete={onDelete}
/>
))}
</ul>
);
}
// TodoItem.jsx
export default function TodoItem({ todo, onToggle, onDelete }) {
return (
<li
style={{
textDecoration: todo.completed ? 'line-through' : 'none',
cursor: 'pointer',
marginBottom: 8,
}}
>
<span onClick={() => onToggle(todo.id)}>{todo.text}</span>
<button
onClick={() => onDelete(todo.id)}
style={{ marginLeft: 12 }}
>
删除
</button>
</li>
);
}
运行项目后,你可以添加任务、标记完成和删除任务。所有状态管理都集中在 App 组件中,子组件通过 Props 呈现和回调通信。
8. 进阶方向建议
掌握上述基础后,你可以进一步探索:
- useReducer:复杂状态逻辑管理。
- useContext:跨组件共享数据(避免 Props 层层传递)。
- 自定义 Hook:封装可复用的状态逻辑。
- React Router:单页应用路由。
- 状态管理库:Redux、Zustand 等。
- 性能优化:React.memo、useMemo、useCallback。
9. 总结
通过这篇教程,你应当理解了 React 的核心概念:
- 组件 是 UI 的积木块,通过 Props 传递数据。
- useState 为组件添加可交互的状态。
- useEffect 管理渲染之外的副作用操作。
实战中的 Todo 应用完整展示了这几部分如何协同工作。React 的世界远不止于此,但坚实的组件、状态与 Hooks 基础是建造任何复杂应用的基石。继续实践,不断构建项目,你的 React 技能会日益精进。