React中useState的setState在哪些条件下会触发组件重渲染?
function App() { const [blah, setBlah] = useState(true); console.log('BLAH', blah); setBlah(true); return <button onClick={() => setBlah(true)}>Blah</button>; }
相同setState调用的行为差异原因
两种场景行为不同,核心是setState的「状态更新跳出(Bailout)」逻辑在不同执行阶段生效规则不同:
- 组件顶层直接调用
setBlah(true)的场景:
此时调用setState时,正处于组件当前渲染周期未结束的状态,React还没有把当前的状态值标记为「已完成提交」,即使新值和当前状态完全一致,也不会触发Bailout逻辑,会直接标记组件需要重新渲染。就会进入「渲染→执行setState→触发重渲染→再执行setState」的无限循环,最终报渲染次数过多的错误。 - 点击按钮调用
setBlah(true)的场景:
点击事件属于渲染完全结束后触发的交互事件,此时状态已经完成提交,React会先对传入的新值和当前已提交的状态做Object.is比较,Object.is(true, true)返回为true,直接触发Bailout逻辑,跳过组件重渲染。
setState传入相同值时触发重渲染的所有场景
当调用setState时,如果新值和当前状态通过Object.is比较相等时,仅以下场景会触发重渲染:
- 状态更新发生在渲染阶段(组件函数执行过程中):包括组件顶层、useMemo计算函数同步调用
setState的场景,此时React不会走Bailout校验,直接触发重渲染 - 使用类组件时,未手动实现
shouldComponentUpdate、也未继承PureComponent:类组件的this.setState默认不会做状态全等校验,即使值相同也会触发重渲染 - 状态为引用类型,虽然内容一致但引用地址不同:比如每次调用
setState传入的是新的引用对象,Object.is比较返回false,自然会触发重渲染
最佳实践建议
- 不要在组件顶层同步调用
setState,该写法本身违反React生命周期规则,极易导致无限循环 - 对于引用类型的状态更新,可以在调用
setState前手动做值比对,确认值发生变化后再执行更新 - 类组件可通过
PureComponent、函数组件可通过React.memo包裹减少不必要的重渲染
内容的提问来源于stack exchange,提问作者Vampiro
相关产品推荐
相关产品推荐

