You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何当useEffect依赖为对象时React会触发三次重渲染?

问题解答

核心原因

第三次渲染的触发点是第二次渲染时useEffect里调用的setTryone(3)——即使此时tryone的当前值已经是3,在你使用的React 17及更早版本中,调用setState传入与当前状态相等的原始类型值时,依然会触发组件重新渲染。

完整流程拆解

  1. 第一次渲染

    • 执行console.log("start"),输出start
    • 创建新对象v1 = { key1: { key2: 2 } },v1.key1是全新的引用
    • 初始化状态tryone = 2
    • 首次渲染触发useEffect,输出=useEffect=,调用setTryone(3)触发第二次渲染
  2. 第二次渲染

    • 执行console.log("start"),输出start
    • 再次创建新对象v1,v1.key1是和第一次完全不同的引用
    • 状态更新为tryone = 3
    • 由于v1.key1引用变化,useEffect依赖数组不相等,再次执行useEffect,输出=useEffect=,调用setTryone(3)
    • 在React 17及更早版本中,即使setState传入的新值和当前状态相等,也会触发第三次渲染
  3. 第三次渲染

    • 执行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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.10 18:55:16