React重渲染触发时机?useState状态更新与闭包疑问解析
问题解答
代码示例
import { useState } from 'react'; export default function Counter() { const [number, setNumber] = useState(0); return ( <> <h1>{number}</h1> <button onClick={() => { setNumber(number + 1); setTimeout(()=>setNumber(number + 1),2000); setTimeout(()=>setNumber(number + 1),5000); }}>+3</button> </> ) }
疑问解答
重渲染实际何时触发?
React在事件处理函数(比如这里的onClick回调)内部会启用批量更新机制:所有在事件函数里调用的setState都会被收集起来,等整个事件函数执行完毕后,才会统一触发一次重渲染。所以你调用setNumber(number + 1)后,React不会立刻重渲染,而是继续执行后面的setTimeout代码,等整个onClick函数跑完,才会处理状态更新并触发重渲染。第一次重渲染完成后,后续回调为何仍无法获取更新后的number?
因为setTimeout的回调函数是在当前渲染周期的onClick函数里创建的,它通过闭包捕获了当前渲染时的number值(也就是初始的0)。哪怕之后组件重渲染,number变成了1,这个回调里的number依然是闭包保存的旧值0——回调属于之前的渲染上下文,不会自动同步新的状态值。为何函数式更新能解决问题?
函数式更新的写法setNumber(prev => prev + 1),会让React在执行这个更新时,自动传入当前最新的状态值作为参数(prev)。不管回调是何时执行的,它都会基于最新的状态去计算新值,而不是依赖闭包里的旧快照。比如第一个setTimeout回调执行时,prev会是当时最新的number值(1),更新后变成2;第二个回调执行时prev是2,更新后变成3,这样就能得到预期的+3效果。
内容的提问来源于stack exchange,提问作者cheems
相关产品推荐
相关产品推荐

