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

如何阻止浏览器调整窗口大小时React组件出现重复渲染问题

窗口resize时避免Header组件反复渲染的解决方案

问题原因

当前代码存在三个核心问题导致频繁重渲染:

  • useWindowSize钩子会在每次窗口resize时触发状态更新,哪怕窗口宽度没有跨过992px的显示阈值,也会导致MainLayout组件重渲染,进而触发子组件重渲染
  • resize事件触发频率极高,未做防抖处理,进一步放大了重渲染的性能损耗
  • 子组件未做渲染优化,父组件每次重渲染都会带动子组件重新执行渲染逻辑

优化代码示例

方案1:针对性阈值判断(更适合当前业务场景)

直接封装仅返回是否符合桌面端阈值的钩子,只在状态切换时更新:

import { useState, useLayoutEffect, memo } from 'react';

// 仅在跨显示阈值时更新状态的自定义钩子
function useIsDesktop(breakpoint = 992) {
  const [isDesktop, setIsDesktop] = useState(window.innerWidth > breakpoint);
  
  useLayoutEffect(() => {
    let debounceTimer = null;
    const updateStatus = () => {
      clearTimeout(debounceTimer);
      // 加100ms防抖,避免resize高频触发逻辑
      debounceTimer = setTimeout(() => {
        const currentStatus = window.innerWidth > breakpoint;
        // 只有状态发生变化时才更新state,减少重渲染触发
        if (currentStatus !== isDesktop) {
          setIsDesktop(currentStatus);
        }
      }, 100);
    };

    window.addEventListener('resize', updateStatus);
    return () => {
      window.removeEventListener('resize', updateStatus);
      clearTimeout(debounceTimer);
    };
  }, [breakpoint, isDesktop]);

  return isDesktop;
}

// 用memo包裹子组件,props无变化时不重渲染
const MobileHeader = memo(() => {
  // 原有组件逻辑保持不变
  return <div>移动端头部</div>;
});

const DesktopHeader = memo(() => {
  // 原有组件逻辑保持不变
  return <div>桌面端头部</div>;
});

const MainLayout = () => {
  const isDesktop = useIsDesktop();
  return (
    <>
      {isDesktop ? <DesktopHeader /> : <MobileHeader />}
    </>
  );
};

方案2:保留通用useWindowSize钩子的优化方案

如果需要在其他场景复用useWindowSize,也可以通过防抖+状态缓存的方式优化:

import { useState, useLayoutEffect, memo, useMemo } from 'react';

// 加了防抖的通用窗口尺寸钩子
function useWindowSize(debounceDelay = 100) {
  const [size, setSize] = useState([window.innerWidth, window.innerHeight]);
  
  useLayoutEffect(() => {
    let debounceTimer = null;
    const updateSize = () => {
      clearTimeout(debounceTimer);
      debounceTimer = setTimeout(() => {
        setSize([window.innerWidth, window.innerHeight]);
      }, debounceDelay);
    };

    window.addEventListener('resize', updateSize);
    return () => {
      window.removeEventListener('resize', updateSize);
      clearTimeout(debounceTimer);
    };
  }, [debounceDelay]);

  return size;
}

// 子组件仍用memo包裹
const MobileHeader = memo(() => <div>移动端头部</div>);
const DesktopHeader = memo(() => <div>桌面端头部</div>);

const MainLayout = () => {
  const [width] = useWindowSize();
  // 用useMemo缓存显示判断结果,仅当width跨阈值时才更新
  const showDesktopHeader = useMemo(() => width > 992, [width]);
  
  return (
    <>
      {showDesktopHeader ? <DesktopHeader /> : <MobileHeader />}
    </>
  );
};

核心优化原理

  1. 防抖处理:resize事件每秒可触发几十次,加100-200ms的防抖可以大幅降低逻辑执行和状态更新的频率,同时不会影响用户体验
  2. 按需更新状态:只有当影响组件渲染的判断结果发生变化时才更新状态,避免无关的状态更新触发整个链路的重渲染
  3. 子组件渲染缓存:用memo包裹子组件后,只要子组件接收的props没有变化,就不会随父组件重渲染,进一步降低性能消耗
  4. 如果后续需要给Header组件传参,建议用useCallback包裹事件函数、useMemo包裹引用类型的props,保证props的稳定性,避免memo失效

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.11 16:21:03