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

为何这段React代码最后一次渲染时未执行useEffect Hooks?

React组件重新渲染但不执行useEffect的原因分析

问题代码

import React, { useState, useEffect, useRef } from "react";
import ReactDOM from "react-dom";

function Example() {
  const [test, setTest] = useState("init");
  const renderNum = useRef(0);

  console.log("renderNum: ", renderNum.current);
  renderNum.current++;

  useEffect(() => {
    console.log("first useEffect value: ", test);
    setTest("newval");
  });

  useEffect(() => {
    console.log("useEffect value: ", test);
  });
  return <div>{test + renderNum.current}</div>;
}

const rootElement = document.getElementById("root");
ReactDOM.render(<Example />, rootElement);

实际输出

renderNum:  0
first useEffect value:  init 
useEffect value:  init 
renderNum:  1
first useEffect value:  newval 
useEffect value:  newval 
renderNum:  2

原因解析

  1. 状态更新的bailout机制:
    当第二次渲染的useEffect中调用setTest("newval")时,test的当前值已经是"newval"——新值与当前状态完全相同。此时React会触发一次bailout渲染:

    • 组件函数会被执行(所以renderNum继续递增,输出renderNum: 2),这是因为React需要执行组件逻辑,对比虚拟DOM是否发生变化。
    • 但React会跳过useEffect这类副作用的执行,因为状态没有实际变更,副作用依赖的核心变量未发生变化,执行副作用属于不必要的性能消耗。
  2. 为什么不直接跳过这次渲染?
    React的设计逻辑是:即使setState传入相同值,也不会直接跳过组件渲染——因为组件输出可能依赖state之外的变量(比如例子中的renderRef),必须执行组件函数后对比虚拟DOM,才能决定是否更新真实DOM。但由于状态本身无变更,React会跳过副作用执行流程。

  3. 关于useEffect的执行条件:
    你的两个useEffect都未设置依赖数组,默认会在每次常规渲染完成后执行,但bailout渲染属于特殊场景——React判定状态无变更,因此跳过了副作用的执行。

内容的提问来源于stack exchange,提问作者Elliot Lynde

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 06:53:11