组件内部状态详解
State定义:State(状态)是组件内部的数据,用于存储组件的动态信息。当State发生变化时,React会自动重新渲染组件以反映最新的状态。
// useState的基本语法
const [state, setState] = useState(initialValue);
// 解析:
// state: 当前的状态值
// setState: 更新状态的函数
// initialValue: 状态的初始值
import { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<h2>计数器: {count}</h2>
<button onClick={() => setCount(count + 1)}>
增加
</button>
<button onClick={() => setCount(count - 1)}>
减少
</button>
<button onClick={() => setCount(0)}>
重置
</button>
</div>
);
}
function StateTypes() {
// 数字类型
const [count, setCount] = useState(0);
// 字符串类型
const [name, setName] = useState('张三');
// 布尔类型
const [isVisible, setIsVisible] = useState(true);
// 数组类型
const [items, setItems] = useState([1, 2, 3]);
// 对象类型
const [user, setUser] = useState({
name: '李四',
age: 25,
email: 'lisi@example.com'
});
return (
<div>
<p>计数: {count}</p>
<p>姓名: {name}</p>
<p>可见: {isVisible ? '是' : '否'}</p>
<p>项目: {items.join(', ')}</p>
<p>用户: {user.name}, {user.age}岁</p>
</div>
);
}
重要警告:永远不要直接修改State!必须使用setState函数来更新状态。
// 直接修改State(错误)
const [count, setCount] = useState(0);
const handleIncrement = () => {
count = count + 1; // 错误!
};
const [items, setItems] = useState([1, 2, 3]);
const handleAdd = () => {
items.push(4); // 错误!直接修改数组
};
// 使用setState(正确)
const [count, setCount] = useState(0);
const handleIncrement = () => {
setCount(count + 1); // 正确
};
const [items, setItems] = useState([1, 2, 3]);
const handleAdd = () => {
setItems([...items, 4]); // 正确
};
何时使用函数式更新:当新状态依赖于前一个状态时,应该使用函数式更新,避免状态更新延迟导致的问题。
function Counter() {
const [count, setCount] = useState(0);
// 普通更新(简单场景)
const incrementSimple = () => {
setCount(count + 1);
};
// 函数式更新(推荐)
const incrementFunctional = () => {
setCount(prevCount => prevCount + 1);
};
// 连续多次更新
const incrementThreeTimes = () => {
setCount(prevCount => prevCount + 1);
setCount(prevCount => prevCount + 1);
setCount(prevCount => prevCount + 1);
};
return (
<div>
<h2>计数: {count}</h2>
<button onClick={incrementSimple}>
简单增加
</button>
<button onClick={incrementFunctional}>
函数式增加
</button>
<button onClick={incrementThreeTimes}>
增加三次
</button>
</div>
);
}
function UserProfile() {
const [user, setUser] = useState({
name: '张三',
age: 25,
email: 'zhangsan@example.com',
address: {
city: '北京',
street: '朝阳区'
}
});
// 更新单个属性
const updateName = () => {
setUser({
...user,
name: '李四'
});
};
// 更新多个属性
const updateMultiple = () => {
setUser({
...user,
name: '王五',
age: 30
});
};
// 更新嵌套属性
const updateAddress = () => {
setUser({
...user,
address: {
...user.address,
city: '上海'
}
});
};
return (
<div>
<h2>用户信息</h2>
<p>姓名: {user.name}</p>
<p>年龄: {user.age}</p>
<p>邮箱: {user.email}</p>
<p>城市: {user.address.city}</p>
<button onClick={updateName}>更新姓名</button>
<button onClick={updateMultiple}>更新多项</button>
<button onClick={updateAddress}>更新地址</button>
</div>
);
}
function TodoList() {
const [todos, setTodos] = useState([
{ id: 1, text: '学习React', completed: false },
{ id: 2, text: '写代码', completed: true }
]);
// 添加新项
const addTodo = () => {
const newTodo = {
id: Date.now(),
text: '新任务',
completed: false
};
setTodos([...todos, newTodo]);
};
// 删除项
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
));
};
return (
<div>
<h2>待办事项</h2>
<button onClick={addTodo}>添加任务</button>
<ul>
{todos.map(todo => (
<li key={todo.id}>
<span
style={{
textDecoration: todo.completed ? 'line-through' : 'none'
}}
onClick={() => toggleTodo(todo.id)}
>
{todo.text}
</span>
<button onClick={() => deleteTodo(todo.id)}>
删除
</button>
</li>
))}
</ul>
</div>
);
}
重要提示:setState是异步的,React会批量处理多个setState调用以提高性能。这意味着在setState之后立即读取state可能得到的是旧值。
function AsyncStateExample() {
const [count, setCount] = useState(0);
const handleClick = () => {
console.log('点击前:', count);
setCount(count + 1);
console.log('setState后:', count);
// 这里的count仍然是旧值!
// 因为setState是异步的
};
const handleClickCorrect = () => {
setCount(prevCount => {
const newCount = prevCount + 1;
console.log('新值:', newCount);
return newCount;
});
};
return (
<div>
<h2>计数: {count}</h2>
<button onClick={handleClick}>
点击我(查看控制台)
</button>
<button onClick={handleClickCorrect}>
正确方式
</button>
</div>
);
}
function MultipleStates() {
const [name, setName] = useState('');
const [age, setAge] = useState(0);
const [email, setEmail] = useState('');
// 每个状态独立更新
const handleNameChange = (e) => {
setName(e.target.value);
};
return (
<form>
<input
value={name}
onChange={handleNameChange}
/>
<!-- 其他输入框 -->
</form>
);
}
function SingleState() {
const [formData, setFormData] = useState({
name: '',
age: 0,
email: ''
});
// 更新时需要展开对象
const handleChange = (e) => {
const { name, value } = e.target;
setFormData({
...formData,
[name]: value
});
};
return (
<form>
<input
name="name"
value={formData.name}
onChange={handleChange}
/>
<!-- 其他输入框 -->
</form>
);
}
选择建议:当状态之间没有关联时,使用多个独立的State;当状态之间有紧密关联时,可以使用单个对象State。
性能优化:如果初始状态需要通过复杂计算得出,可以使用函数作为useState的参数,这样函数只在组件首次渲染时执行一次。
// ❌ 不推荐:每次渲染都会计算
function ExpensiveInitialization() {
const [data, setData] = useState(
calculateExpensiveData() // 每次渲染都执行
);
// ...
}
// ✅ 推荐:只在首次渲染时计算
function LazyInitialization() {
const [data, setData] = useState(() =>
calculateExpensiveData() // 只执行一次
);
// ...
}
// 示例:从localStorage读取初始值
function LocalStorageExample() {
const [user, setUser] = useState(() => {
const savedUser = localStorage.getItem('user');
return savedUser ? JSON.parse(savedUser) : null;
});
// ...
}
任务:创建一个购物车组件
要求:
// 参考答案
function ShoppingCart() {
const [products] = useState([
{ id: 1, name: 'iPhone', price: 5999 },
{ id: 2, name: 'MacBook', price: 9999 },
{ id: 3, name: 'iPad', price: 3299 }
]);
const [cart, setCart] = useState([]);
const addToCart = (product) => {
setCart(prevCart => {
const existingItem = prevCart.find(item => item.id === product.id);
if (existingItem) {
return prevCart.map(item =>
item.id === product.id
? { ...item, quantity: item.quantity + 1 }
: item
);
}
return [...prevCart, { ...product, quantity: 1 }];
});
};
const removeFromCart = (productId) => {
setCart(cart.filter(item => item.id !== productId));
};
const updateQuantity = (productId, quantity) => {
if (quantity <= 0) {
removeFromCart(productId);
return;
}
setCart(cart.map(item =>
item.id === productId
? { ...item, quantity }
: item
));
};
const totalPrice = cart.reduce(
(sum, item) => sum + item.price * item.quantity,
0
);
return (
<div>
<h2>商品列表</h2>
{products.map(product => (
<div key={product.id}>
<span>{product.name} - ¥{product.price}</span>
<button onClick={() => addToCart(product)}>
添加到购物车
</button>
</div>
))}
<h2>购物车</h2>
{cart.map(item => (
<div key={item.id}>
<span>{item.name} - ¥{item.price}</span>
<input
type="number"
value={item.quantity}
onChange={(e) => updateQuantity(item.id, parseInt(e.target.value))}
min="0"
/>
<button onClick={() => removeFromCart(item.id)}>
移除
</button>
</div>
))}
<h3>总价: ¥{totalPrice}</h3>
</div>
);
}