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

RTK Query结合SignalR WebSocket使用问题求助

React中RTK Query结合SignalR实现实时同步的问题

我正尝试在React应用中使用RTK Query结合SignalR WebSocket服务,实现与服务器数据的实时同步。

我的RTK API代码如下:

export const hubConnection = new signalR.HubConnectionBuilder()
    .withUrl("https://localhost:7152/messagehub", {
        withCredentials: false,
        skipNegotiation: true,
        logMessageContent: true,
        logger: signalR.LogLevel.Information,
        transport: signalR.HttpTransportType.WebSockets
    })
    .withAutomaticReconnect([0, 1000, 5000, 10000, 30000])
    .build();

export const apiSlice = createApi({
    reducerPath: 'apiSlice',
    baseQuery: fetchBaseQuery({ baseUrl: 'https://localhost:7152' }),
    endpoints: (builder) => ({
        // these are the websocket / signalR endpoints
        getNotifications: builder.query<NotificationType[], void>({
            query: () => 'messagehub',
            async onCacheEntryAdded(
                arg,
                { updateCachedData, cacheDataLoaded, cacheEntryRemoved },
            ) {
                console.log(hubConnection.state); // PRINTS Disconnected
                try {
                    // this loads the existing cache data
                    // await cacheDataLoaded; IF I UNCOMMENT THIS LINE, NOTHING FURTHER HAPPENS
                    if (hubConnection.state === signalR.HubConnectionState.Disconnected) {
                        console.log('SignalR connection is disconnected. Starting connection...');  // THIS MESSAGE PRINTS TO CONSOLE
                        await hubConnection.start().then(() => console.log("SignalR connection established")); // THIS MESSAGE PRINTS TO CONSOLE
                    }
                
                    hubConnection.on("ReceiveMessage", (notification: NotificationType) => {
                        console.log('StatusMessage', notification); // WITH THE cacheDataLoaded CALL COMMENTED OUT, THIS PRINTS WHEN A MESSAGE IS RECEIVED
                        updateCachedData((draft) => {
                            console.log('cache updating', draft); // THIS MESSAGE NEVER PRINTS
                            draft.push(notification) // THE STORE STAYS EMPTY
                        })
                    });
                } catch (error) {
                    console.error('Failed to connect to SignalR hub:', JSON.parse(JSON.stringify(error)));
                }
                // this is a promise that resolves when the socket is no longer being used
                await cacheEntryRemoved;
                // close signalR connection
                await hubConnection.stop();
            }
        }),
    }),
})

目前遇到以下问题:

  • 保留await cacheDataLoaded时后续逻辑完全阻塞;注释后刷新出现SignalR的"Connection ID required"错误,无法定位原因。
  • Redux DevTools中apiSlice.queries.getNotifications状态为status: rejected,但控制台无错误日志。
  • SignalR发送测试消息时,ReceiveMessage能打印消息,但updateCachedData从未触发,缓存为空。

我曾实现SignalR作为中间件或单组件使用,但希望基于RTK Query实现,现陷入困境,恳请解决方案。


解决方案

问题1:await cacheDataLoaded阻塞 & "Connection ID required"错误

  • cacheDataLoaded阻塞原因:你的query: () => 'messagehub'是向SignalR Hub发送普通HTTP请求,但SignalR Hub不处理这类请求,导致初始查询一直处于pending状态,cacheDataLoaded永远无法resolve,后续代码被阻塞。
  • 解决办法:
    1. 替换query为queryFn,直接返回初始空数组作为缓存初始值,避免无效HTTP请求:
      getNotifications: builder.query<NotificationType[], void>({
          queryFn: () => ({ data: [] }), // 初始化缓存为空数组
          async onCacheEntryAdded(...) {
              // 现在可以正常使用await cacheDataLoaded,不会阻塞
              await cacheDataLoaded;
              // ... 原有逻辑
          }
      })
      
    2. 移除skipNegotiation: true配置:该配置仅当服务器明确支持无协商WebSocket连接时有效,否则会导致"Connection ID required"错误。让SignalR自动执行协商流程获取连接ID:
      .withUrl("https://localhost:7152/messagehub", {
          withCredentials: false,
          logMessageContent: true,
          logger: signalR.LogLevel.Information,
          transport: signalR.HttpTransportType.WebSockets
          // 移除skipNegotiation: true
      })
      

问题2:查询状态为rejected但无错误日志

这是因为初始query: () => 'messagehub'的HTTP请求失败,但onCacheEntryAdded的catch仅捕获SignalR连接错误,未处理初始查询的失败。改用queryFn返回成功结果后,查询状态会变为fulfilled,该问题自动解决。

问题3:updateCachedData不触发,缓存为空

  • 根本原因:初始查询失败导致缓存从未被初始化,updateCachedData无法修改未初始化的缓存。
  • 解决办法:
    1. 通过queryFn返回初始空数组,确保缓存被正确初始化为NotificationType[]类型。
    2. 避免全局共享单个hubConnection实例:多次订阅查询时,复用同一个连接会导致状态冲突。建议在onCacheEntryAdded内部创建连接实例:
      async onCacheEntryAdded(
          arg,
          { updateCachedData, cacheDataLoaded, cacheEntryRemoved },
      ) {
          let connection: signalR.HubConnection;
          try {
              await cacheDataLoaded;
              // 每次订阅创建新的连接实例
              connection = new signalR.HubConnectionBuilder()
                  .withUrl("https://localhost:7152/messagehub", {
                      withCredentials: false,
                      logMessageContent: true,
                      logger: signalR.LogLevel.Information,
                      transport: signalR.HttpTransportType.WebSockets
                  })
                  .withAutomaticReconnect([0, 1000, 5000, 10000, 30000])
                  .build();
      
              await connection.start();
              console.log("SignalR connection established");
      
              connection.on("ReceiveMessage", (notification: NotificationType) => {
                  console.log('StatusMessage', notification);
                  updateCachedData((draft) => {
                      console.log('cache updating', draft);
                      draft.push(notification);
                  });
              });
          } catch (error) {
              console.error('Failed to connect to SignalR hub:', error);
          }
      
          await cacheEntryRemoved;
          if (connection) {
              await connection.stop();
          }
      }
      

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 15:55:57