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

如何在组件卸载时获取更新后的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的引用:

  1. 修改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];
};
  1. 在父组件中调用该方法:
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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.12 03:21:31