You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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函数,所以:

  1. 用useCallback包裹updateBox,明确它的依赖项(layoutState和boxStyle),这样只有当依赖变化时,才会生成新的updateBox。
  2. 把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,都能拿到最新值:

  1. 创建一个ref,每次layoutState更新时,把最新值同步到ref里。
  2. 在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.13 08:18:01