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 下,即使在 setTimeout、Promise 等异步回调里,多次状态更新通常也会被自动合并。
所以旧题里“异步场景不会批处理”的说法要加版本前提。
2. 不要把“异步”理解成 Promise
面试里更准确的表述是:
状态更新是异步调度的,不等于它一定返回 Promise,而是 React 不会在当前同步栈里立刻把你看到的状态变量改掉。
典型题标准答法
问:为什么函数式更新更稳?
答:因为它基于队列里的最新状态计算,而不是捕获当前闭包里的旧值,连续更新和异步回调下都更安全。
问:什么时候能拿到更新后的值?
答:要在下一轮 render 中拿,或者在 effect、回调、DOM 提交之后的正确阶段拿,不能指望调用后当前栈里立刻同步变。
易错点
- 只会背“setState 是异步的”,但解释不清原因。
- 混淆“异步调度”和“Promise 异步”。
- 忽视 React 18 的自动批处理变化。
速记要点
- 依赖旧状态时用函数式更新。
- 当前 render 读到的是状态快照。
- React 18 异步场景里也常会自动批处理。