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

React.js移除StrictMode后组件仍两次渲染的原因排查

React 组件两次渲染原因说明

你现在看到的两次渲染是React的正常执行逻辑,不是异常bug,执行流程如下:

  • 第一次渲染:组件首次挂载,执行组件顶层所有代码,打印初始memberArea值0,挂载完成后执行useEffect中注册的getData接口请求
  • 第二次渲染:接口返回数据后调用setMemberArray更新状态,状态变更触发组件重渲染,再次执行顶层代码打印memberArea值,这个流程和React严格模式无关,只要你调用状态更新方法就会触发重渲染

不要把正常的状态更新重渲染当成问题,React本身就是靠状态变化触发重渲染来更新视图的,只要没有出现渲染死循环、渲染次数远超预期的情况,不需要强行消除重渲染。

现有代码的潜在问题(可能触发更多次非预期渲染)

  1. useEffect依赖项包含memberArea,只要memberArea值发生变动,就会重新触发接口请求,请求返回后更新状态再次触发重渲染。如果其他逻辑里意外修改了memberArea(哪怕重复设置相同的0值,React17及更早版本不会做优化跳过渲染),就会出现额外渲染。
  2. getData定义在组件内部,每次渲染都会生成新的函数引用,如果后续把getData加入其他Hook的依赖数组,会意外触发依赖更新,导致多余执行。
  3. 你贴的代码里memberNum没有定义来源,如果这个值是从props或其他状态派生的,取值逻辑不对也可能触发重复请求。
  4. 没有做组件卸载判断,如果请求返回前组件已经卸载,调用setMemberArray会触发内存泄漏警告。

优化后参考代码

// 不依赖组件内部状态的请求逻辑移到组件外,避免重复生成函数引用
const fetchMemberData = async (memberCode: string) => {
  try {
    const res = await axios.get(`api/v1/member?memberCode=${memberCode}`);
    return res.data.data.memberList;
  } catch (err) {
    console.log(err);
    return [];
  }
};

// 组件内部逻辑
const YourComponent = () => {
  const [memberArray, setMemberArray] = useState<any[]>([]);
  const [memberArea, setMemberArea] = useState(0);
  // 替换成你实际的memberNum来源,比如从props、路由参数、其他状态获取
  const memberNum = '';

  console.log("memberArea: " + memberArea);

  useEffect(() => {
    let isUnmounted = false;
    fetchMemberData(memberNum).then(list => {
      if (!isUnmounted) {
        setMemberArray([...list]);
      }
    });
    // 卸载时标记,避免内存泄漏
    return () => {
      isUnmounted = true;
    };
  }, [memberArea, memberNum]);

  return /* 你的组件视图 */ null;
}

额外渲染排查方向

如果实际渲染次数超过2次,按以下顺序排查:

  • 检查父组件是否重渲染:当前组件如果没有用React.memo包裹,父组件每次重渲染都会连带子组件重渲染,和子组件自身逻辑无关
  • 检查是否在组件顶层无判断直接调用状态更新方法,这种情况会触发渲染死循环
  • 检查memberArea是否被重复赋值相同值,React18之前的版本不会自动跳过相同原始值的更新渲染

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 04:09:25