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

React中useWindowUnloadEffect回调无法获取更新后的state问题

解决React中beforeunload回调无法获取最新state的问题

问题根源

你遇到的是闭包陷阱导致的状态获取异常:

  • useWindowUnloadEffect内部的useEffect依赖为空数组,仅在组件首次渲染时绑定beforeunload事件,此时传入的disconnectSocket函数捕获的是初始状态的currentUser(null)。
  • 后续currentUser更新时,disconnectSocket会生成新的函数实例,但useWindowUnloadEffect未监听handler的变化,不会重新绑定事件,因此触发beforeunload时执行的仍是旧函数,无法获取最新的currentUser。

解决方案

方法一:修复自定义钩子依赖+用useCallback包裹回调

  1. 更新useWindowUnloadEffect,将handler加入依赖数组,确保回调更新时重新绑定事件:
// useWindowUnloadEffect.tsx
import { useEffect } from "react";
type HandlerType = () => void;

const useWindowUnloadEffect = (handler: HandlerType) => {
  useEffect(() => {
    window.addEventListener("beforeunload", handler);

    return () => {
      window.removeEventListener("beforeunload", handler);
    };
  }, [handler]); // 新增handler作为依赖
};

export default useWindowUnloadEffect;
  1. 在App.tsx中用useCallback包裹disconnectSocket,让它在currentUser变化时重新生成:
// App.tsx
import { useContext, useEffect, useCallback } from "react";

function App() {
  const { currentUser } = useContext(UserContext);

  console.log(currentUser);

  // 用useCallback包裹,依赖currentUser
  const disconnectSocket = useCallback(() => {
    socket.emit("disconnected", {
      room: "room_1",
      user: currentUser?.name,
      test: "hi",
    });
  }, [currentUser]); // 状态更新时函数重新生成

  useWindowUnloadEffect(disconnectSocket);

  // 其余原有代码保持不变
}

方法二:使用useRef保存最新状态

如果不想频繁重新绑定事件,可以用useRef实时同步currentUser的最新值,旧回调也能通过ref获取最新状态:

// App.tsx
import { useContext, useEffect, useRef } from "react";

function App() {
  const { currentUser } = useContext(UserContext);
  const currentUserRef = useRef(currentUser);

  // 同步最新状态到ref
  useEffect(() => {
    currentUserRef.current = currentUser;
  }, [currentUser]);

  console.log(currentUser);

  function disconnectSocket() {
    socket.emit("disconnected", {
      room: "room_1",
      user: currentUserRef.current?.name, // 从ref中获取最新值
      test: "hi",
    });
  }

  useWindowUnloadEffect(disconnectSocket);

  // 其余原有代码保持不变
}

此方法下useWindowUnloadEffect可保持原有空依赖配置。


内容的提问来源于stack exchange,提问作者Prasadbro

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 11:05:37