如何将窗口resize事件监听值正确赋值给React State?
问题根源
这是React Hooks的经典闭包陷阱问题:
- 你给
useEffect设置了空依赖数组,因此该Effect仅会在组件首次挂载时执行1次 - 首次执行时绑定的
autoResize函数属于首次渲染的作用域,内部读取的isDesktopSize永远是初始值true,即使后续state更新,旧的autoResize也无法读取到最新的状态值 - 原代码没有添加事件监听的卸载逻辑,组件销毁时会造成内存泄漏
修复方案
优化方案(推荐)
因为状态更新逻辑仅依赖窗口宽度,不需要读取旧的isDesktopSize值,我们可以直接移除不必要的依赖,避免函数重复生成,性能最优:
import React, {useState,useEffect, useCallback} from 'react' import LeftSideNavbar from './LeftSideNavbar' import TopNavbar from './TopNavbar' export default function PostLayout({children}) { const [isDesktopSize, setIsDesktopSize] = useState(true) const autoResize = useCallback(() => { const currentIsDesktop = window.innerWidth >= 768 console.log("Desktop: " + currentIsDesktop); console.log(window.innerWidth); setIsDesktopSize(currentIsDesktop) }, []) // 无外部依赖,函数仅生成一次 useEffect(() => { window.addEventListener('resize', autoResize) autoResize(); // 组件卸载时移除事件监听,避免内存泄漏 return () => window.removeEventListener('resize', autoResize) }, [autoResize]) return ( <> <TopNavbar isDesktopSize={isDesktopSize}/> <main> <LeftSideNavbar/> {children} </main> </> ) }
通用适配方案
如果你的业务逻辑确实需要读取旧的isDesktopSize状态,可以将状态加入依赖,配合useCallback保证事件监听可以正常读取最新状态:
import React, {useState,useEffect, useCallback} from 'react' import LeftSideNavbar from './LeftSideNavbar' import TopNavbar from './TopNavbar' export default function PostLayout({children}) { const [isDesktopSize, setIsDesktopSize] = useState(true) const autoResize = useCallback(() => { console.log("Desktop: " + isDesktopSize); console.log(window.innerWidth); setIsDesktopSize(window.innerWidth >= 768) }, [isDesktopSize]) // 依赖state,state更新时自动生成新的函数 useEffect(() => { window.addEventListener('resize', autoResize) autoResize(); return () => window.removeEventListener('resize', autoResize) }, [autoResize]) // 函数更新时自动重新绑定事件 return ( <> <TopNavbar isDesktopSize={isDesktopSize}/> <main> <LeftSideNavbar/> {children} </main> </> ) }
内容的提问来源于stack exchange,提问作者siwm
相关产品推荐
相关产品推荐

