主题
概念卡片:React 状态管理与 setState
一句话机制:React 没有像 Vue 那样的数据监听(没有 defineProperty / Proxy),状态更新必须显式调用
setState告知 React「数据变了」;setState在合成事件/生命周期里是异步批量合并的(为性能),且依赖不可变数据(生成新引用)才能配合PureComponent/memo的浅比较跳过无意义渲染。
为什么必须 setState
js
this.state.message = '改了' // ❌ React 不知道数据变了,不渲染
this.setState({ message: '改了' }) // ✅ 触发 render
setState是从Component继承来的,内部调用this.updater.enqueueSetState(...)把更新入队。
setState 的三个关键行为
| 行为 | 说明 |
|---|---|
| 异步 | 合成事件/生命周期里异步(批量更新);setTimeout/原生 DOM 事件里同步 |
| 合并 | 传对象:多次调用只生效一次(Object.assign 合并);传函数 (state)=>...:依次执行 |
| 不可变 | 直接 push 再 setState 引用没变,浅比较跳过;应 [...arr, x] 生成新引用 |
js
// 传对象:多个 setState 合并,counter 只 +1
this.setState({ counter: this.state.counter + 1 })
this.setState({ counter: this.state.counter + 1 })
// 传函数:依次执行,counter +2
this.setState((state) => ({ counter: state.counter + 1 }))
this.setState((state) => ({ counter: state.counter + 1 }))性能优化链:SCU / PureComponent / memo
js
// 类组件:继承 PureComponent(内部对 props/state 浅比较)
class Main extends PureComponent { ... }
// 函数组件:用 memo 包裹
const MemoHeader = memo(function Header() { ... })底层是
!shallowEqual(oldProps, newProps) || !shallowEqual(oldState, newState),所以浅比较要求数据不可变。
不可变数据的力量
js
// ❌ push 原地改,引用没变,PureComponent 不更新
this.state.friends.push({ name: 'why' })
this.setState({ friends: this.state.friends })
// ✅ 扩展运算符生成新数组引用,触发更新
this.setState({ friends: [...this.state.friends, { name: 'why' }] })列表 key 的规则
- key 必须唯一且稳定(别用随机数——下次 render 会重新生成)。
- 用
index作 key 没有性能优化,且插入/删除时会错位。
不变量(必须成立的约束)
- 直接
this.state.xxx = ...不触发渲染,必须setState。 setState后不能立即读新值 → 用第二个参数回调,或componentDidUpdate。- Diff 算法三规则:同层比较(不跨层)、不同类型重建子树、key 保持节点稳定。
踩坑案例
- 现象:数组
push后再setState,界面不刷新。 原因:PureComponent浅比较,push没改引用。解决:[...arr, x]生成新引用。
常见误解
- 以为
setState一定是异步 →setTimeout和原生 DOM 事件里是同步的。 - 以为「多个 setState 都会 +1」 → 传对象会合并成一次,要 +N 须传函数。
关联
- 组件化:概念卡片:React组件化与JSX
- Hook 版状态:概念卡片:ReactHook
- 总览:React技术栈总览
- 源:
B40-资源/语雀-Java开发/前端技术/✅React全家桶/♂️React基本使用/✅React系列八-深入理解setState