React Hooks窗口Resize时状态被初始值覆盖问题求助
问题分析与解决方案
嘿,我来帮你拆解这个问题——这其实是React Hooks里很常见的闭包陷阱导致的,咱们一步步捋清楚:
为什么resize时layoutState会"回到初始值"?
你在组件挂载时(那个空依赖数组的useEffect)给window绑定了resize事件监听,对应的处理函数是updateBox。但这里的关键是:这个updateBox是组件第一次渲染时创建的,它捕获了当时的layoutState——也就是初始的initLayout。
当你在输入框里修改数值时,layoutState会更新,组件重新渲染,这时候会生成一个新的updateBox函数(里面引用的是最新的layoutState),但之前绑定在resize事件上的还是旧的那个updateBox!所以当窗口resize触发时,调用的是旧的updateBox,它里面的layoutState自然还是初始值,看起来就像是被覆盖了一样。
怎么解决?
有两种常用的方案,你可以选一种适合你的:
方案1:用useCallback包裹updateBox,同步事件监听
我们需要让resize事件能拿到最新的updateBox函数,所以:
- 用
useCallback包裹updateBox,明确它的依赖项(layoutState和boxStyle),这样只有当依赖变化时,才会生成新的updateBox。 - 把
updateBox加入到绑定resize事件的useEffect依赖数组里,每次updateBox更新时,先移除旧监听,再绑定新的。
修改后的关键代码:
import React,{useState,useEffect,useRef, useCallback} from 'react'; // ...其他代码不变 const updateBox = useCallback(() => { console.log("updateBox",layoutState.size.width,":",layoutState.size.height) let percentage = (layoutState.size.height/layoutState.size.width) * 100 if(percentage > 100){ percentage = 100 } percentage = percentage+"%" const cloneBoxStyle = clone(boxStyle) cloneBoxStyle.width = "100%" cloneBoxStyle.height = percentage setBoxStyle(cloneBoxStyle) }, [layoutState, boxStyle]); // 声明依赖项 useEffect(() =>{ console.log("Component did mount") window.addEventListener("resize", updateBox); return () => { window.removeEventListener("resize", updateBox); }; }, [updateBox]); // 依赖更新时重新绑定监听
方案2:用useRef保存最新的layoutState
如果不想频繁更新事件监听,也可以用ref来同步最新状态,这样不管什么时候调用updateBox,都能拿到最新值:
- 创建一个ref,每次
layoutState更新时,把最新值同步到ref里。 - 在
updateBox里用ref.current获取最新的layoutState。
修改后的关键代码:
const Layout = ({props}) => { const boxContainer = useRef(null); const [layoutState,setLayoutState] = useState(initLayout) const layoutStateRef = useRef(layoutState); // 创建状态ref // 同步layoutState到ref useEffect(() => { layoutStateRef.current = layoutState; }, [layoutState]); // ...其他代码不变 const updateBox = () => { // 用ref获取最新状态 const currentLayout = layoutStateRef.current; console.log("updateBox",currentLayout.size.width,":",currentLayout.size.height) let percentage = (currentLayout.size.height/currentLayout.size.width) * 100 if(percentage > 100){ percentage = 100 } percentage = percentage+"%" const cloneBoxStyle = clone(boxStyle) cloneBoxStyle.width = "100%" cloneBoxStyle.height = percentage setBoxStyle(cloneBoxStyle) } // 绑定resize的useEffect无需修改 useEffect(() =>{ console.log("Component did mount") window.addEventListener("resize", updateBox); return () => { window.removeEventListener("resize", updateBox); }; }, []); }
验证效果
两种方案都能解决问题:输入框修改数值后,resize窗口时,updateBox会使用最新的layoutState计算比例,不会再回到初始值了。你可以根据自己的习惯选择其中一种~
内容的提问来源于stack exchange,提问作者Trevor
相关产品推荐
相关产品推荐

