使用React Hooks时resize触发后dimensions与clipPath不更新如何解决?
问题根源&修复方案
你最初版本的问题是典型的React闭包陷阱:你给useEffect配置的依赖是空数组,导致回调函数里引用的dimensions永远是组件首次渲染时的初始值,哪怕你调用setDimensions更新了状态,闭包拿到的还是旧值。同时你把sidebar写成普通函数每次调用都生成新的状态和事件监听器,逻辑设计也不合理。
你更新后的代码还有几个问题需要修复:
- 自定义Hook
getDimensions的事件监听器无法正常卸载:你addEventListener和removeEventListener传入的是两个不同的匿名函数,引用地址不一致,导致卸载时根本删不掉之前绑定的监听器,需要把回调函数提出来单独定义。 - 代码里
MobileNavWrapper组件传的custom={height}是错误的:你前面从getDimensions解构出来的变量是windowHeight,如果没有额外定义height变量的话这里传的就是undefined,值根本不对。 - Framer Motion的variants默认只在动画状态切换时重新计算,你窗口resize的时候如果菜单的开/关状态没有变化,它不会重新计算
clipPath,给MobileNavBackground加一个和窗口高度绑定的key,强制它在高度变化时重新渲染,就能拿到最新的高度值计算样式。
优化后代码
首先修改getDimensions,修复事件卸载问题并添加防抖优化性能:
// getDimensions.ts import { useState, useEffect } from 'react'; function debounce(func: Function, wait: number) { let timeout: NodeJS.Timeout; return (...args: any[]) => { clearTimeout(timeout); timeout = setTimeout(() => func.apply(this, args), wait); }; } export const getDimensions = () => { const [dimension, setDimension] = useState([window.innerWidth, window.innerHeight]); useEffect(() => { const handleResize = debounce(() => { setDimension([window.innerWidth, window.innerHeight]) }, 100); window.addEventListener("resize", handleResize); return () => window.removeEventListener("resize", handleResize) }, []); return dimension; };
然后修改Navbar.tsx,修正传值问题,添加key强制更新:
// Navbar.tsx const Navbar: React.FC<NavbarProps> = () => { const [windowWidth, windowHeight] = getDimensions(); // 原有逻辑... return ( <MobileNavWrapper initial={false} animate={menuIsOpen ? "open" : "closed"} custom={windowHeight} ref={ref} menuIsOpen={menuIsOpen} > <MobileNavBackground key={windowHeight} variants={sidebar} custom={windowHeight} /> <MobileNav menuIsOpen={menuIsOpen} toggleMenu={toggleMenu} /> <MenuToggle toggle={() => toggleMenu()} /> </MobileNavWrapper> ) }
你现有的sidebar配置是正确的,无需修改。
内容的提问来源于stack exchange,提问作者Lars van der Niet
相关产品推荐
相关产品推荐

