为何当useEffect依赖为对象时React会触发三次重渲染?
问题解答
核心原因
第三次渲染的触发点是第二次渲染时useEffect里调用的setTryone(3)——即使此时tryone的当前值已经是3,在你使用的React 17及更早版本中,调用setState传入与当前状态相等的原始类型值时,依然会触发组件重新渲染。
完整流程拆解
第一次渲染
- 执行
console.log("start"),输出start - 创建新对象
v1 = { key1: { key2: 2 } },v1.key1是全新的引用 - 初始化状态
tryone = 2 - 首次渲染触发
useEffect,输出=useEffect=,调用setTryone(3)触发第二次渲染
- 执行
第二次渲染
- 执行
console.log("start"),输出start - 再次创建新对象
v1,v1.key1是和第一次完全不同的引用 - 状态更新为
tryone = 3 - 由于
v1.key1引用变化,useEffect依赖数组不相等,再次执行useEffect,输出=useEffect=,调用setTryone(3) - 在React 17及更早版本中,即使
setState传入的新值和当前状态相等,也会触发第三次渲染
- 执行
第三次渲染
- 执行
console.log("start"),输出start - 又一次创建新的
v1.key1引用 - 理论上
useEffect会因依赖变化再次执行,但调用setTryone(3)时状态已是3,不会触发第四次渲染(你看到的结果里没有第三次=useEffect=可能是遗漏,或是React在某些场景下的优化行为)
- 执行
解决方案
要避免这种不必要的渲染,你可以:
- 将
v1.key1的定义移到组件外部,确保每次渲染时引用不变:const key1Ref = { key2: 2 }; // 定义在组件外部 function App() { console.log("start"); const v1 = { key1: key1Ref }; const [tryone, setTryone] = useState(2); useEffect(() => { console.log("=useEffect="); setTryone(3); }, [v1.key1]); return ( <div className="App"> <h1>Hello CodeSandbox</h1> </div> ); } - 或者使用
useMemo缓存v1.key1,保证引用稳定:function App() { console.log("start"); const v1 = useMemo(() => ({ key1: { key2: 2 } }), []); const [tryone, setTryone] = useState(2); useEffect(() => { console.log("=useEffect="); setTryone(3); }, [v1.key1]); return ( <div className="App"> <h1>Hello CodeSandbox</h1> </div> ); } - 升级到React 18,React 18中会对原始类型的状态更新做浅比较,相等时跳过渲染
内容的提问来源于stack exchange,提问作者Boison
相关产品推荐
相关产品推荐

