⚡setState 是同步还是异步?90% 前端答错,5 秒让你秒懂!

55 阅读2分钟

面试 3 连问:

  1. setState 是同步还是异步?
  2. 为什么连续 setState 只渲染一次?
  3. 怎样在 setState 后拿到最新 DOM?
    读完这篇,你可以把答案倒背如流。

一、setState 到底是什么?

一句话:
setState 是 React 用来「排队」更新组件状态的函数,最终触发一次批量渲染。

它有两种形态:

形态语法场景
对象式setState({count: 1})不关心旧状态
函数式setState(prev => ({count: prev.count + 1}))依赖旧状态

二、同步还是异步?看「调用环境」!

React 18 以前:

调用位置表现示例
React 事件异步批量onClick={() => { setState({a: 1}); console.log(this.state.a) }} // 仍是旧值
原生事件 / setTimeout / Promise同步setTimeout(() => setState({a: 1}), 0) // 立即更新

React 18 默认全部走异步批量(createRoot),除非 flushSync


三、批量更新原理:Transaction + 队列

// 伪代码
enqueueSetState(state) {
  this.pendingQueue.push(state);
  if (!this.isBatching) {
    this.flush();
  }
}
  • 事件回调里 React 开启一个 transaction,所有 setState 先排队。
  • 事务结束 → 一次性合并 → 触发一次 render → 减少重渲染

四、连续 setState 的正确姿势

// ❌ 错误:只加 1,因为三次合并都是基于同一个 baseState
this.setState({ count: this.state.count + 1 });
this.setState({ count: this.state.count + 1 });
this.setState({ count: this.state.count + 1 });

// ✅ 正确:函数式让每次拿到最新 prevState
this.setState(prev => ({ count: prev.count + 1 }));
this.setState(prev => ({ count: prev.count + 1 }));
this.setState(prev => ({ count: prev.count + 1 }));
// 最终 count = +3

五、setState 回调 & flushSync:拿最新 DOM

// 1️⃣ 回调用法(类组件)
this.setState({ text: 'hello' }, () => {
  console.log('DOM 已更新', this.ref.current);
});

// 2️⃣ flushSync(函数组件)
import { flushSync } from 'react-dom';
flushSync(() => setValue(100));
console.log(inputRef.current.value); // 100

六、函数组件里的 useState 与 setState 异同

特性useStatesetState
批量更新
回调❌(用 useEffect 代替)
函数式更新setCount(c => c + 1)同左
合并直接替换浅合并

七、一次完整生命周期穿行

class Demo extends React.Component {
  state = { count: 0 };

  handleClick = () => {
    this.setState({ count: this.state.count + 1 });
    console.log('同步 log:', this.state.count); // 旧值
  };

  render() {
    console.log('render with count:', this.state.count);
    return <button onClick={this.handleClick}>{this.state.count}</button>;
  }
}

输出顺序:
同步 log: 0render with count: 1


八、高频面试问答速记卡

问题一句话答案
setState 后立即能拿到最新 state 吗?不能,除非在 flushSync 或回调里。
为什么推荐函数式 setState?保证基于最新状态计算
React 18 之后 setState 都是异步吗?默认异步,flushSync 强制同步。

九、总结

  • setState 本质是“排队调度”,不是立即写 DOM。
  • 函数式更新 = 状态安全
  • 想拿最新 DOM → 用回调 / flushSync

背下这张卡片,下次面试官再问 setState,你可以 反问他:想听同步还是异步场景?