跳到主要内容

React setState经典面试题

面试速答

  • 以下内容以 React 18 为准。
  • 这题高频考法几乎都围绕 3 件事:
    • setState/状态更新是不是同步
    • 连续多次更新结果是什么
    • 对象式更新和函数式更新有什么差别

经典题 1:连续两次加一,结果是多少

const [count, setCount] = useState(0)

function handleClick() {
setCount(count + 1)
setCount(count + 1)
}

很多人直觉会答 2,但通常结果是 1。

原因是这两次更新都拿到了同一次 render 里的 count,也就是同一个旧值 0

更稳的写法是:

setCount(c => c + 1)
setCount(c => c + 1)

这时结果才会是 2。

经典题 2:为什么更新后马上打印还是旧值

setCount(count + 1)
console.log(count)

打印出来还是旧值,因为当前函数执行时拿到的是这一轮 render 的状态快照。更新已经入队,但还没进入下一轮 render。

经典题 3:类组件里的对象式 setState

this.setState({count: this.state.count + 1})
this.setState({count: this.state.count + 1})

如果这两次都基于同一个旧状态计算,最终也可能只加一次。

类组件里同样推荐在依赖旧状态时使用函数式写法:

this.setState(prev => ({count: prev.count + 1}))

React 18 下的补充点

1. 自动批处理范围变大

React 18 下,即使在 setTimeoutPromise 等异步回调里,多次状态更新通常也会被自动合并。

所以旧题里“异步场景不会批处理”的说法要加版本前提。

2. 不要把“异步”理解成 Promise

面试里更准确的表述是:

状态更新是异步调度的,不等于它一定返回 Promise,而是 React 不会在当前同步栈里立刻把你看到的状态变量改掉。

典型题标准答法

问:为什么函数式更新更稳?

答:因为它基于队列里的最新状态计算,而不是捕获当前闭包里的旧值,连续更新和异步回调下都更安全。

问:什么时候能拿到更新后的值?

答:要在下一轮 render 中拿,或者在 effect、回调、DOM 提交之后的正确阶段拿,不能指望调用后当前栈里立刻同步变。

易错点

  • 只会背“setState 是异步的”,但解释不清原因。
  • 混淆“异步调度”和“Promise 异步”。
  • 忽视 React 18 的自动批处理变化。

速记要点

  • 依赖旧状态时用函数式更新。
  • 当前 render 读到的是状态快照。
  • React 18 异步场景里也常会自动批处理。