class Counter extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0,
name: '计数器'
};
}
// 或者使用类属性语法
state = {
count: 0,
name: '计数器'
};
}
**读取 State**:
class Counter extends React.Component {
state = { count: 0 };
render() {
return {this.state.count};
}
}
**更新 State**:
class Counter extends React.Component {
state = { count: 0 };
increment = () => {
this.setState({ count: this.state.count + 1 });
};
render() {
return (
{this.state.count}
);
}
}
**State 更新的合并**:
setState 会将提供的对象浅合并到当前 state。
// state = { count: 0, name: 'test' }
this.setState({ count: 1 });
// state 变为 { count: 1, name: 'test' }
// name 没有被改变
**基于前一个 State 更新**:
由于 setState 可能是异步的,依赖前一个 state 时应该使用函数形式。
// 可能不正确
this.setState({ count: this.state.count + 1 });
// 正确
this.setState((prevState) => ({
count: prevState.count + 1
}));
// 使用第二个参数 props
this.setState((prevState, props) => ({
count: prevState.count + props.increment
}));
**State 更新后的回调**:
setState 的第二个参数是回调函数,在 state 更新并重新渲染后执行。
this.setState(
{ count: this.state.count + 1 },
() => {
console.log('Count updated:', this.state.count);
}
);
===== 6.3 函数组件中的 State(Hooks) =====
useState Hook 让函数组件也能使用 state。
**基本用法**:
import { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
return (
{count}
);
}
**useState 返回数组**:
* 第一个元素:当前的 state 值
* 第二个元素:更新 state 的函数
**初始值**:
useState 的参数是初始值,只在首次渲染时使用。
// 简单初始值
const [count, setCount] = useState(0);
// 函数式初始值(适用于复杂计算)
const [state, setState] = useState(() => {
const initialState = someExpensiveComputation();
return initialState;
});
**更新 State**:
// 直接设置新值
setCount(5);
// 基于前一个 state 更新
setCount(prevCount => prevCount + 1);
// 更新对象
const [user, setUser] = useState({ name: '张三', age: 25 });
// 错误:直接修改
user.age = 26;
setUser(user); // 不会触发重新渲染
// 正确:创建新对象
setUser({ ...user, age: 26 });
// 使用函数式更新
setUser(prevUser => ({ ...prevUser, age: prevUser.age + 1 }));
**多个 State**:
function Form() {
const [name, setName] = useState('');
const [email, setEmail] = useState('');
const [age, setAge] = useState(0);
// 或者使用一个对象
const [form, setForm] = useState({
name: '',
email: '',
age: 0
});
}
===== 6.4 State 的不可变性 =====
React 中的 state 应该被视为不可变的。
**为什么不可变性很重要**:
* 让 React 的更新检测更高效
* 避免意外的副作用
* 让时间旅行调试成为可能
* 保持数据流清晰
**处理数组**:
const [items, setItems] = useState(['a', 'b', 'c']);
// 添加元素
setItems([...items, 'd']);
// 插入元素
setItems([...items.slice(0, 1), 'x', ...items.slice(1)]);
// 删除元素
setItems(items.filter((_, index) => index !== 1));
// 修改元素
setItems(items.map((item, index) =>
index === 1 ? 'modified' : item
));
// 排序(先复制)
setItems([...items].sort());
**处理对象**:
const [user, setUser] = useState({
name: '张三',
address: {
city: '北京',
street: '长安街'
}
});
// 更新顶层属性
setUser({ ...user, name: '李四' });
// 更新嵌套对象(注意深拷贝问题)
setUser({
...user,
address: { ...user.address, city: '上海' }
});
// 或者使用 immer
import produce from 'immer';
setUser(produce(user, draft => {
draft.address.city = '上海';
}));
===== 6.5 Props vs State =====
| 特性 | Props | State |
|------|-------|-------|
| 来源 | 父组件传递 | 组件内部管理 |
| 可读性 | 只读 | 可读写 |
| 修改 | 不能修改 | 通过 setState 修改 |
| 作用域 | 父子组件通信 | 组件内部 |
| 触发渲染 | props 变化触发重新渲染 | state 变化触发重新渲染 |
**数据流**:
* Props 自上而下流动(父到子)
* State 是组件内部的
* 子组件通过回调函数影响父组件的 state
===== 6.6 状态提升 =====
当多个组件需要共享状态时,将状态提升到它们的最近公共祖先组件。
// 温度转换器示例
function BoilingVerdict({ celsius }) {
if (celsius >= 100) {
return 水会沸腾
;
}
return 水不会沸腾
;
}
class Calculator extends React.Component {
state = { temperature: '' };
handleChange = (e) => {
this.setState({ temperature: e.target.value });
};
render() {
const { temperature } = this.state;
return (
);
}
}
**状态提升的步骤**:
1. 找到需要共享 state 的组件
2. 找到它们的最近公共祖先
3. 将 state 定义在祖先组件中
4. 通过 props 将 state 传递给子组件
5. 通过回调函数让子组件修改 state
===== 6.7 派生 State =====
某些情况下,state 可以从 props 派生出来。
**反模式:复制 props 到 state**:
// 不要这样做
class EmailInput extends React.Component {
state = {
email: this.props.email // 复制 props 到 state
};
// 问题:props 更新时 state 不会更新
}
**正确使用派生 state**:
// 方法 1:完全受控组件
function EmailInput({ email, onChange }) {
return ;
}
// 方法 2:使用 key 强制重新挂载
// 方法 3:只在 prop 真正变化时更新 state
class EmailInput extends React.Component {
state = {
email: this.props.defaultEmail
};
componentDidUpdate(prevProps) {
if (prevProps.defaultEmail !== this.props.defaultEmail) {
this.setState({ email: this.props.defaultEmail });
}
}
}
===== 6.8 批量更新 =====
React 18 引入了自动批处理,多个 state 更新会被合并为一次重新渲染。
function App() {
const [count, setCount] = useState(0);
const [flag, setFlag] = useState(false);
function handleClick() {
setCount(c => c + 1); // 不会立即重新渲染
setFlag(f => !f); // 不会立即重新渲染
// React 会等到所有 state 更新完成后再重新渲染
}
console.log('Render'); // 只会打印一次
return (
);
}
**flushSync**:
如果需要强制同步更新,可以使用 flushSync(不推荐常规使用):
import { flushSync } from 'react-dom';
function handleClick() {
flushSync(() => {
setCount(c => c + 1);
});
// 到这里 DOM 已经更新了
flushSync(() => {
setFlag(f => !f);
});
}
===== 6.9 总结 =====
本章深入讲解了 Props 和 State:
* State 的概念和使用场景
* 类组件和函数组件中的 state 管理
* State 的不可变性
* Props vs State 的区别
* 状态提升模式
* 派生 state 的处理
* 批量更新机制
理解 state 是 React 开发的核心,下一章我们将学习事件处理。