如何在组件卸载时获取更新后的Context值以关闭WebSocket连接?
解决组件卸载时获取Context最新状态并断开WebSocket的问题
问题场景
父组件作为仪表盘,根据条件渲染订阅WebSocket服务的子组件。用户在子组件完成表单提交后,数据通过WebSocket发送并完成连接,但当用户导航到其他页面(如个人资料页)时,WebSocket连接并未断开,用户仍显示为已连接状态。
尝试处理时遇到的核心问题:
- 断开WebSocket需要
stompClient(来自stompjs的客户端)和用户信息data,两者存储在全局Context中 - 使用
useEffect依赖空数组时,组件卸载时拿到的是Context的默认空数组,无法获取更新后的真实数据 - 若将
data加入useEffect依赖,每次data变化都会触发清理函数,不符合“仅卸载时执行一次”的需求 - 子组件会因WebSocket返回值频繁卸载重挂载,无法在子组件内处理断开逻辑;尝试将
data存入本地状态、组件间传递函数等方法均无效
解决方案
方法1:用useRef保存最新Context状态
利用useRef的.current属性始终保留最新值的特性,规避闭包陷阱,确保组件卸载时能拿到最新的stomp和data:
import { useStateProfConsultFilterValue } from '../../../context/profConsultFilterState'; import { useEffect, useRef } from 'react'; // ... const [{ stomp, data }, dispatch] = useStateProfConsultFilterValue(); const connectionRef = useRef({ stomp, data }); // 实时同步Context的最新状态到ref useEffect(() => { connectionRef.current = { stomp, data }; }, [stomp, data]); // 仅在组件卸载时执行断开逻辑 useEffect(() => { return () => { const { stomp, data } = connectionRef.current; // 根据实际业务判断是否需要断开(比如检查stomp是否存在、data是否有效) if (stomp && data?.length) { // 执行WebSocket断开逻辑,示例: // stomp.unsubscribe(data.subscriptionId); // stomp.disconnect(); console.log('断开WebSocket连接,用户数据:', data); } }; }, []); // 依赖为空数组,仅卸载时触发一次清理函数
方法2:在Context中封装断开连接方法
把WebSocket的断开逻辑统一封装到Context的状态管理中,父组件直接调用封装好的方法,无需自行维护stomp和data的引用:
- 修改Context的状态管理文件(
profConsultFilterState.js):
import { useReducer } from 'react'; const initialState = { stomp: null, data: [] }; const reducer = (state, action) => { switch (action.type) { // ...保留原有其他action类型 case 'DISCONNECT_WEBSOCKET': if (state.stomp) { // 执行断开操作:取消订阅、断开客户端连接 state.stomp.unsubscribe(state.data.subscriptionId); state.stomp.disconnect(); } return { ...state, stomp: null, data: [] }; default: return state; } }; export const useStateProfConsultFilterValue = () => { const [state, dispatch] = useReducer(reducer, initialState); // 封装断开WebSocket的方法 const disconnectWebSocket = () => { dispatch({ type: 'DISCONNECT_WEBSOCKET' }); }; return [{ ...state }, dispatch, disconnectWebSocket]; };
- 在父组件中调用该方法:
import { useStateProfConsultFilterValue } from '../../../context/profConsultFilterState'; import { useEffect } from 'react'; // ... const [{ stomp, data }, dispatch, disconnectWebSocket] = useStateProfConsultFilterValue(); useEffect(() => { return () => { disconnectWebSocket(); // 直接调用Context中的断开方法 }; }, [disconnectWebSocket]); // 依赖仅为封装好的方法,不会频繁触发
方案说明
- 方法1适合快速修复,通过
useRef同步最新状态,解决闭包导致的旧值问题 - 方法2更符合状态管理的单一职责原则,将WebSocket的连接、断开逻辑统一维护在Context中,避免组件分散处理
内容的提问来源于stack exchange,提问作者alexgarciaalcuadrado
相关产品推荐
相关产品推荐

