Skip to content

概念卡片: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 须传函数。

关联

最近更新