如何阻止浏览器调整窗口大小时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 />} </> ); };
核心优化原理
- 防抖处理:resize事件每秒可触发几十次,加100-200ms的防抖可以大幅降低逻辑执行和状态更新的频率,同时不会影响用户体验
- 按需更新状态:只有当影响组件渲染的判断结果发生变化时才更新状态,避免无关的状态更新触发整个链路的重渲染
- 子组件渲染缓存:用
memo包裹子组件后,只要子组件接收的props没有变化,就不会随父组件重渲染,进一步降低性能消耗 - 如果后续需要给Header组件传参,建议用
useCallback包裹事件函数、useMemo包裹引用类型的props,保证props的稳定性,避免memo失效
内容的提问来源于stack exchange,提问作者mrtyvz61
相关产品推荐
相关产品推荐

