React中状态变更未在resize回调中生效,动态调整块宽度遇问题
React窗口resize时动态调整区块宽度的问题修复
问题根源
你的代码存在三个核心问题:
- 闭包陷阱:
useEffect依赖为空数组,导致onResize函数捕获的myWidth始终是初始值200,状态更新后回调无法获取最新值,判断逻辑彻底失效。 - 无防抖处理:resize事件触发频率极高,窗口每变化一个像素就会执行一次回调,导致控制台频繁打印。
- 未清理事件监听:组件卸载时没有移除resize监听,可能引发内存泄漏。
修正后的代码
function X({ title }) { const [myWidth, setMyWidth] = React.useState(200); // 记录上一次的窗口阈值状态,避免重复触发调整逻辑 const isOverThreshold = React.useRef(false); // 封装防抖的resize回调,减少触发频率 const debouncedResize = React.useCallback( React.useMemo(() => { let timeoutId; return () => { clearTimeout(timeoutId); timeoutId = setTimeout(() => { const currentOver = window.innerWidth > 1000; // 仅当阈值状态发生变化时,才执行宽度调整 if (currentOver !== isOverThreshold.current) { isOverThreshold.current = currentOver; if (currentOver) { console.log(`Make Bigger:`, myWidth); setMyWidth(400); } else { console.log(`Make Smaller:`, myWidth); setMyWidth(200); } } }, 100); // 100ms防抖间隔,可根据需求调整 }; }, [myWidth]), [myWidth] ); React.useEffect(() => { window.addEventListener('resize', debouncedResize); // 组件卸载时清理事件监听,避免内存泄漏 return () => window.removeEventListener('resize', debouncedResize); }, [debouncedResize]); // 初始渲染时主动检查窗口宽度,确保初始状态匹配当前屏幕 React.useEffect(() => { const initialOver = window.innerWidth > 1000; isOverThreshold.current = initialOver; if (initialOver) { setMyWidth(400); } }, []); return ( <div> <div style={{ backgroundColor: 'red', width: myWidth, height: 100, }} > Hello </div> </div> ); } const el = document.querySelector("#root"); ReactDOM.render(<X title='Example Component' />, el);
关键改进说明
- 闭包问题解决:通过
useCallback和useMemo确保回调能获取最新的myWidth状态,同时用useRef独立记录阈值状态,避免依赖组件状态做重复判断。 - 防抖优化:添加100ms延迟,只有窗口停止调整后才执行逻辑,彻底解决频繁触发的问题。
- 内存泄漏预防:在
useEffect的返回函数中移除resize监听,组件卸载时自动清理。 - 初始状态校正:组件挂载时主动检查窗口宽度,确保初始渲染的区块尺寸符合当前屏幕阈值。
内容的提问来源于stack exchange,提问作者Hairi
相关产品推荐
相关产品推荐

