为何React在该示例中触发3次渲染而非2次?
为什么React会触发第三次渲染?
以下是示例代码:
const {useEffect, useState} = React; const App = () => { const [timer, setTimer] = useState(0); const addTimer = () => { console.log('timer', timer); setTimer(timer + 1); } useEffect(() => { console.log('Use effect'); setInterval(addTimer, 8000); }, []) return <h5>Time: {console.log('Render') || timer}</h5>; }; ReactDOM.createRoot(document.querySelector('#root')).render(<App />);
对应的HTML:
<div id="root"></div> <script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.1.0/umd/react.production.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.1.0/umd/react-dom.production.min.js"></script>
控制台输出:
Render Use effect timer 0 Render timer 0 Render timer 0 (infinity)
已知前提:
- 首次
Render是组件初始渲染,useEffect执行后启动定时器 addTimer因闭包问题始终读取初始的timer=0,此部分无需解释- 第一次定时器触发时,调用
setTimer(0+1)将状态从0更新为1,触发第二次Render - 第二次定时器触发时,调用
setTimer(0+1)尝试将状态从1更新为1(值未变化),但仍触发了第三次Render,之后每次定时器触发仅输出timer 0不再触发渲染
核心原因解释
React的状态更新机制中,即使新状态和当前状态值相同,首次遇到该情况时仍会触发一次组件渲染函数执行——这是因为React默认不会对状态值做前置的「浅比较」来跳过渲染流程,只有在确认新旧状态完全一致后,后续的重复更新才会被优化跳过。
具体到这个场景:
- 第二次定时器触发时,调用
setTimer(1)(闭包中timer始终为0),此时组件当前状态已经是1,表面看值未变化 - React会先将这次更新加入队列,触发完整的渲染流程,因此组件渲染函数执行,输出第三次
Render - 在渲染的后期阶段,React对比新旧状态发现值完全一致,最终不会更新DOM
- 后续定时器再次触发时,同样调用
setTimer(1),React会识别到这是重复的无意义更新,直接跳过渲染流程,因此不再输出Render
这种行为是React的设计逻辑:优先保证状态更新流程的完整性,仅在重复遇到相同值的状态更新时,才会启动优化跳过渲染。
注:该现象在React 16.8.0、17.0.0、18.1.0版本中均存在,与版本无关。
内容的提问来源于stack exchange,提问作者EzioMercer
相关产品推荐
相关产品推荐

