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,后续代码被阻塞。- 解决办法:
- 替换
query为queryFn,直接返回初始空数组作为缓存初始值,避免无效HTTP请求:getNotifications: builder.query<NotificationType[], void>({ queryFn: () => ({ data: [] }), // 初始化缓存为空数组 async onCacheEntryAdded(...) { // 现在可以正常使用await cacheDataLoaded,不会阻塞 await cacheDataLoaded; // ... 原有逻辑 } }) - 移除
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无法修改未初始化的缓存。 - 解决办法:
- 通过
queryFn返回初始空数组,确保缓存被正确初始化为NotificationType[]类型。 - 避免全局共享单个
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
相关产品推荐
相关产品推荐

