关于React组件重复渲染与无限渲染的两个技术疑问
关于React组件重复渲染与无限渲染的两个技术疑问
第一个问题:点击按钮为何只触发两次重渲染?
先看你提供的代码:
import React,{useState,useEffect} from 'react' export function App(){ const [a,setA]=useState(0) const handleClick = () => { setA(12) } console.log("rerendered") return <> <button onClick={handleClick}>Increment</button> {a} </> }
咱们一步步拆解这个过程:
- 组件首次挂载时,会执行一次完整渲染,此时
console.log("rerendered")打印第一次,页面上a显示0。 - 当你点击按钮时,触发
handleClick回调调用setA(12),此时状态a从0更新为12,状态变化会触发组件重新渲染,这是第二次打印,页面上的a也同步更新为12。 - 之后再点击按钮,你还是调用
setA(12),但此时a的当前值已经是12了。React内部会对新旧状态做浅比较,发现两者完全一致,就会跳过这次无意义的重渲染,不会再执行组件函数,自然也就不会打印"rerendered"了。这是React内置的性能优化手段,避免不必要的渲染消耗。
第二个问题:直接在组件内调用setA为何会触发无限渲染?
再看这段代码:
import React,{useState,useEffect} from 'react' export function App(){ const [a,setA]=useState(0) setA(12) return <> {a} </> }
这个情况和第一个问题的核心差异在于调用setState的时机:
- 组件首次挂载时,执行函数体代码:先通过
useState(0)初始化a为0,然后立刻调用setA(12)——这会触发组件重新渲染。 - 第二次渲染时,组件函数再次执行:这里要注意,
useState的初始值只在首次挂载时生效,第二次渲染时a的当前值其实是12,但你又在组件渲染的同步代码里调用了setA(12)。和事件回调里的setState不同,在**渲染阶段同步调用setState**时,React不会即时做浅比较优化——哪怕你设置的是和当前相同的值,也会标记组件需要重新渲染。于是就陷入了「渲染 -> 调用setA触发重渲染 -> 再次渲染 -> 再次调用setA」的无限循环。
简单总结:事件回调中的setState会先对比新旧状态,相同则跳过;但在组件渲染阶段同步执行的setState,哪怕值没变化,也会触发重渲染,最终导致无限循环。
备注:内容来源于stack exchange,提问作者user23957365
相关产品推荐
相关产品推荐

