React 状态 State
概述
状态(State)是 React 组件中可以随时间变化的数据。状态使组件能够响应用户交互、网络请求或其他事件。本章将学习如何在函数组件中使用 useState Hook 管理状态。
🔄 什么是状态
状态 vs Props
jsx
// Props 是从外部传入的,只读的
function Greeting({ name }) {
return <h1>Hello, {name}!</h1>;
}
// State 是组件内部的,可变的
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>当前计数: {count}</p>
<button onClick={() => setCount(count + 1)}>
增加
</button>
</div>
);
}状态的特点
- 局部性:每个组件实例都有自己的状态
- 异步更新:状态更新可能是异步的
- 触发重新渲染:状态改变会导致组件重新渲染
🎣 useState Hook
基础用法
jsx
import { useState } from 'react';
function SimpleCounter() {
// [状态值, 更新函数] = useState(初始值)
const [count, setCount] = useState(0);
const increment = () => setCount(count + 1);
const decrement = () => setCount(count - 1);
const reset = () => setCount(0);
return (
<div>
<h2>计数器: {count}</h2>
<button onClick={increment}>+1</button>
<button onClick={decrement}>-1</button>
<button onClick={reset}>重置</button>
</div>
);
}不同类型的状态
jsx
function StateDemoApp() {
// 数字状态
const [age, setAge] = useState(25);
// 字符串状态
const [name, setName] = useState('');
// 布尔值状态
const [isVisible, setIsVisible] = useState(true);
// 数组状态
const [items, setItems] = useState(['苹果', '香蕉']);
// 对象状态
const [user, setUser] = useState({
name: '张三',
email: 'zhangsan@example.com'
});
const addItem = () => {
setItems([...items, `新项目 ${items.length + 1}`]);
};
const updateUser = () => {
setUser({
...user,
name: '李四'
});
};
return (
<div>
<h3>姓名: {name}</h3>
<input
value={name}
onChange={(e) => setName(e.target.value)}
placeholder="输入姓名"
/>
<h3>年龄: {age}</h3>
<button onClick={() => setAge(age + 1)}>增加年龄</button>
<button onClick={() => setIsVisible(!isVisible)}>
{isVisible ? '隐藏' : '显示'}内容
</button>
{isVisible && (
<div>
<h3>项目列表:</h3>
<ul>
{items.map((item, index) => (
<li key={index}>{item}</li>
))}
</ul>
<button onClick={addItem}>添加项目</button>
</div>
)}
<h3>用户: {user.name} ({user.email})</h3>
<button onClick={updateUser}>更新用户</button>
</div>
);
}🔄 状态更新模式
函数式更新
jsx
function Counter() {
const [count, setCount] = useState(0);
const increment = () => {
// 基于之前的状态值更新
setCount(prevCount => prevCount + 1);
};
const incrementByFive = () => {
// 连续更新时使用函数式更新
setCount(prev => prev + 1);
setCount(prev => prev + 1);
setCount(prev => prev + 1);
setCount(prev => prev + 1);
setCount(prev => prev + 1);
};
return (
<div>
<p>计数: {count}</p>
<button onClick={increment}>+1</button>
<button onClick={incrementByFive}>+5</button>
</div>
);
}对象状态更新
jsx
function UserProfile() {
const [user, setUser] = useState({
name: '',
email: '',
age: 0,
preferences: {
theme: 'light',
language: 'zh'
}
});
const updateName = (name) => {
setUser(prevUser => ({
...prevUser,
name
}));
};
const updateEmail = (email) => {
setUser(prevUser => ({
...prevUser,
email
}));
};
const updatePreferences = (newPrefs) => {
setUser(prevUser => ({
...prevUser,
preferences: {
...prevUser.preferences,
...newPrefs
}
}));
};
return (
<div>
<input
placeholder="姓名"
value={user.name}
onChange={(e) => updateName(e.target.value)}
/>
<input
placeholder="邮箱"
value={user.email}
onChange={(e) => updateEmail(e.target.value)}
/>
<select
value={user.preferences.theme}
onChange={(e) => updatePreferences({ theme: e.target.value })}
>
<option value="light">浅色主题</option>
<option value="dark">深色主题</option>
</select>
<div>
<h3>用户信息:</h3>
<p>姓名: {user.name}</p>
<p>邮箱: {user.email}</p>
<p>主题: {user.preferences.theme}</p>
</div>
</div>
);
}数组状态更新
jsx
function TodoList() {
const [todos, setTodos] = useState([
{ id: 1, text: '学习 React', completed: false },
{ id: 2, text: '写代码', completed: true }
]);
const [newTodo, setNewTodo] = useState('');
const addTodo = () => {
if (newTodo.trim()) {
setTodos(prevTodos => [
...prevTodos,
{
id: Date.now(),
text: newTodo,
completed: false
}
]);
setNewTodo('');
}
};
const toggleTodo = (id) => {
setTodos(prevTodos =>
prevTodos.map(todo =>
todo.id === id
? { ...todo, completed: !todo.completed }
: todo
)
);
};
const deleteTodo = (id) => {
setTodos(prevTodos =>
prevTodos.filter(todo => todo.id !== id)
);
};
return (
<div>
<div>
<input
value={newTodo}
onChange={(e) => setNewTodo(e.target.value)}
placeholder="添加新任务"
/>
<button onClick={addTodo}>添加</button>
</div>
<ul>
{todos.map(todo => (
<li key={todo.id}>
<input
type="checkbox"
checked={todo.completed}
onChange={() => toggleTodo(todo.id)}
/>
<span style={{
textDecoration: todo.completed ? 'line-through' : 'none'
}}>
{todo.text}
</span>
<button onClick={() => deleteTodo(todo.id)}>删除</button>
</li>
))}
</ul>
</div>
);
}🎛️ 复杂状态管理
使用 useReducer
jsx
import { useReducer } from 'react';
// 定义 reducer 函数
function counterReducer(state, action) {
switch (action.type) {
case 'increment':
return { count: state.count + 1 };
case 'decrement':
return { count: state.count - 1 };
case 'reset':
return { count: 0 };
case 'set':
return { count: action.payload };
default:
throw new Error(`未知的 action 类型: ${action.type}`);
}
}
function AdvancedCounter() {
const [state, dispatch] = useReducer(counterReducer, { count: 0 });
return (
<div>
<p>计数: {state.count}</p>
<button onClick={() => dispatch({ type: 'increment' })}>
+1
</button>
<button onClick={() => dispatch({ type: 'decrement' })}>
-1
</button>
<button onClick={() => dispatch({ type: 'reset' })}>
重置
</button>
<button onClick={() => dispatch({ type: 'set', payload: 100 })}>
设为 100
</button>
</div>
);
}自定义 Hook 封装状态逻辑
jsx
// 自定义 Hook:表单状态管理
function useForm(initialValues) {
const [values, setValues] = useState(initialValues);
const [errors, setErrors] = useState({});
const handleChange = (name, value) => {
setValues(prev => ({
...prev,
[name]: value
}));
// 清除错误
if (errors[name]) {
setErrors(prev => ({
...prev,
[name]: ''
}));
}
};
const handleSubmit = (onSubmit, validationRules = {}) => {
return (e) => {
e.preventDefault();
const newErrors = {};
Object.keys(validationRules).forEach(field => {
const rule = validationRules[field];
if (rule.required && !values[field]) {
newErrors[field] = `${rule.label || field} 是必填项`;
}
});
setErrors(newErrors);
if (Object.keys(newErrors).length === 0) {
onSubmit(values);
}
};
};
const reset = () => {
setValues(initialValues);
setErrors({});
};
return {
values,
errors,
handleChange,
handleSubmit,
reset
};
}
// 使用自定义 Hook
function ContactForm() {
const { values, errors, handleChange, handleSubmit, reset } = useForm({
name: '',
email: '',
message: ''
});
const onSubmit = (formData) => {
console.log('提交表单:', formData);
alert('表单提交成功!');
reset();
};
const validationRules = {
name: { required: true, label: '姓名' },
email: { required: true, label: '邮箱' },
message: { required: true, label: '消息' }
};
return (
<form onSubmit={handleSubmit(onSubmit, validationRules)}>
<div>
<input
placeholder="姓名"
value={values.name}
onChange={(e) => handleChange('name', e.target.value)}
/>
{errors.name && <span className="error">{errors.name}</span>}
</div>
<div>
<input
type="email"
placeholder="邮箱"
value={values.email}
onChange={(e) => handleChange('email', e.target.value)}
/>
{errors.email && <span className="error">{errors.email}</span>}
</div>
<div>
<textarea
placeholder="消息"
value={values.message}
onChange={(e) => handleChange('message', e.target.value)}
/>
{errors.message && <span className="error">{errors.message}</span>}
</div>
<button type="submit">提交</button>
<button type="button" onClick={reset}>重置</button>
</form>
);
}⚡ 状态性能优化
状态分割
jsx
// ❌ 所有状态都在一个对象中
function BadComponent() {
const [state, setState] = useState({
user: null,
posts: [],
loading: false,
theme: 'light',
sidebar: false
});
// 任何一个字段的更新都会导致整个组件重新渲染
}
// ✅ 将不相关的状态分开
function GoodComponent() {
const [user, setUser] = useState(null);
const [posts, setPosts] = useState([]);
const [loading, setLoading] = useState(false);
const [theme, setTheme] = useState('light');
const [sidebar, setSidebar] = useState(false);
// 每个状态独立更新,减少不必要的重新渲染
}使用 useMemo 和 useCallback 优化
jsx
import { useState, useMemo, useCallback } from 'react';
function OptimizedList({ items }) {
const [filter, setFilter] = useState('');
const [sortBy, setSortBy] = useState('name');
// 缓存过滤和排序的结果
const filteredAndSortedItems = useMemo(() => {
const filtered = items.filter(item =>
item.name.toLowerCase().includes(filter.toLowerCase())
);
return filtered.sort((a, b) => {
if (sortBy === 'name') {
return a.name.localeCompare(b.name);
}
return a[sortBy] - b[sortBy];
});
}, [items, filter, sortBy]);
// 缓存事件处理函数
const handleFilterChange = useCallback((e) => {
setFilter(e.target.value);
}, []);
const handleSortChange = useCallback((e) => {
setSortBy(e.target.value);
}, []);
return (
<div>
<input
placeholder="搜索..."
value={filter}
onChange={handleFilterChange}
/>
<select value={sortBy} onChange={handleSortChange}>
<option value="name">按名称排序</option>
<option value="price">按价格排序</option>
</select>
<ul>
{filteredAndSortedItems.map(item => (
<li key={item.id}>
{item.name} - ${item.price}
</li>
))}
</ul>
</div>
);
}📝 本章小结
状态管理是 React 应用的核心,正确使用状态能够构建响应式和交互式的用户界面。
关键要点
- ✅ useState Hook 用于管理组件状态
- ✅ 状态更新是异步的,使用函数式更新确保正确性
- ✅ 对象和数组状态需要不可变更新
- ✅ useReducer 适合复杂状态逻辑
- ✅ 自定义 Hook 可以封装状态逻辑
- ✅ 状态分割和缓存可以优化性能
最佳实践
- 保持状态最小化:只存储必要的状态
- 状态结构扁平化:避免深层嵌套
- 使用函数式更新:确保状态更新的正确性
- 合理分割状态:提高组件性能
- 封装复杂逻辑:使用自定义 Hook
继续学习:下一章 - React 事件处理