React中useWindowUnloadEffect回调无法获取更新后的state问题
解决React中beforeunload回调无法获取最新state的问题
问题根源
你遇到的是闭包陷阱导致的状态获取异常:
useWindowUnloadEffect内部的useEffect依赖为空数组,仅在组件首次渲染时绑定beforeunload事件,此时传入的disconnectSocket函数捕获的是初始状态的currentUser(null)。- 后续
currentUser更新时,disconnectSocket会生成新的函数实例,但useWindowUnloadEffect未监听handler的变化,不会重新绑定事件,因此触发beforeunload时执行的仍是旧函数,无法获取最新的currentUser。
解决方案
方法一:修复自定义钩子依赖+用useCallback包裹回调
- 更新
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;
- 在
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
相关产品推荐
相关产品推荐

