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

React Native加载时重复订阅WebSocket 需实现手动单订阅

React Native 连接 Rails ActionCable 重复订阅问题修复

问题根源

你的代码存在几个核心问题导致重复订阅:

  • 组件每次重新渲染时,identifier都会生成新UUID,进而创建大量不同名称的频道
  • 全局创建的actionCable和cable实例可能在组件多次挂载时重复初始化
  • 回调函数依赖数组不完整,导致重复触发订阅逻辑
  • 断开连接时的清理逻辑不彻底,残留旧订阅实例

修改后的完整代码

import React, { useState, useEffect, useCallback, useMemo } from 'react';
import { View, Text } from 'react-native';
import { Button } from 'react-native-paper';
import { ActionCable, Cable } from '@kesha-antonov/react-native-action-cable';
import UUID from 'react-native-uuid';

const App: React.FC = () => {
  const [isWebsocketConnected, setIsWebsocketConnected] = useState(false);
  const [dchannel, setDchannel] = useState<any>(null);
  
  // 组件生命周期内仅生成一次唯一标识
  const identifier = useMemo(() => UUID.v4(), []);
  
  // 确保ActionCable消费者和Cable实例只初始化一次
  const actionCable = useMemo(() => {
    return ActionCable.createConsumer('wss://blabla.ngrok-free.app/cable');
  }, []);
  
  const cable = useMemo(() => new Cable({}), []);

  const handleReceived = useCallback(({ type, message }) => {
    console.log(type);
    console.log(message);
    console.log("triggered");
  }, []);

  const handleConnected = useCallback(() => {
    // 防止重复创建订阅
    if (isWebsocketConnected || dchannel) return;
    
    const channel = cable.setChannel(
      `wire_channel_${identifier}`,
      actionCable.subscriptions.create({
        channel: 'WireChannel',
      })
    );
    
    // 绑定消息接收监听
    channel.on('received', handleReceived);
    
    setDchannel(channel);
    setIsWebsocketConnected(true);
  }, [isWebsocketConnected, dchannel, identifier, cable, actionCable, handleReceived]);

  const removeChannel = useCallback(() => {
    if (!dchannel) return;
    
    const channelName = `wire_channel_${identifier}`;
    const channel = cable.channel(channelName);
    
    if (channel) {
      channel.removeListener('received', handleReceived);
      channel.unsubscribe();
      delete cable.channels[channelName];
    }
    
    setDchannel(null);
  }, [dchannel, identifier, cable, handleReceived]);

  const handleDisconnected = useCallback(() => {
    removeChannel();
    setIsWebsocketConnected(false);
  }, [removeChannel]);

  const handleSendMessage = useCallback(() => {
    if (!dchannel) return;
    
    dchannel.perform('speak', { message: 'Hey', identifier: identifier });
  }, [dchannel, identifier]);

  // 组件卸载时自动清理订阅
  useEffect(() => {
    return () => {
      removeChannel();
    };
  }, [removeChannel]);

  return (
    <View style={{ flex: 1 }}>
      <View style={{ padding: 20, backgroundColor: '#f0f0f0', alignItems: 'center' }}>
        <Text style={{ fontSize: 24 }}>Your Logo Here</Text>
      </View>
      <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center', gap: 16 }}>
        <Button
          mode="contained"
          onPress={handleConnected}
          color="#00C853"
          disabled={isWebsocketConnected}
        >
          Connect WebSocket
        </Button>
        <Button
          mode="contained"
          onPress={handleDisconnected}
          color="#FF1744"
          disabled={!isWebsocketConnected}
        >
          Disconnect WebSocket
        </Button>
        <Button
          mode="contained"
          onPress={handleSendMessage}
          disabled={!isWebsocketConnected}
        >
          Send Message
        </Button>
        <Text>
          WebSocket Status: {isWebsocketConnected ? 'Connected' : 'Disconnected'}
        </Text>
      </View>
    </View>
  );
};

export default App;

关键修改说明

  • 持久化唯一标识:用useMemo生成identifier,确保组件生命周期内只生成一次,避免创建大量无关频道
  • 实例单例化:用useMemo包裹actionCable和cable创建逻辑,保证只初始化一次连接实例
  • 连接逻辑防护:在handleConnected中增加状态判断,防止重复触发订阅
  • 完整依赖数组:给所有useCallback补充完整依赖,避免因依赖缺失导致的逻辑重复执行
  • 彻底清理逻辑:在removeChannel中移除监听、取消订阅并删除频道实例,组件卸载时自动执行清理
  • 发送逻辑优化:直接使用已保存的dchannel实例,避免重复查找频道,同时增加空值判断防止报错

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 21:53:24