存储MQTT客户端到Redux Toolkit reducer报错,求替代方案
问题描述
尝试将MQTT客户端存入@redux-toolkit的reducer中时,收到错误提示:A non-serializable value was detected。需要在onCacheEntryAdded函数中使用该MQTT客户端更新缓存,想知道有没有比存入reducer更优的实现方案。
相关代码:
const client = MQTT(socketUrl, options) client.stream.on("error", (err) => { toast.error(`Connection to ${socketUrl} failed`) client.end() return }) dispatch(updateClient(client))
onCacheEntryAdded函数代码:
async onCacheEntryAdded( arg, { updateCachedData, cacheDataLoaded, cacheEntryRemoved, getState } ) { try { const state = getState() const client = state.inbox.client await cacheDataLoaded client.on("message", (topic, data) => { const message = JSON.parse(data.toString()) updateCachedData((draft) => { if (!message.payload) return draft.messages.unshift(message.payload) }) }) } catch (err) {} await cacheEntryRemoved },
可行解决方案
方案1:单例模式管理MQTT客户端
把MQTT客户端做成全局单例,不用存入Redux状态,既不会触发序列化错误,也能在需要的地方直接调用。
实现示例:
// mqttClient.js let clientInstance = null; export const getMqttClient = (socketUrl, options) => { if (!clientInstance) { clientInstance = MQTT(socketUrl, options); clientInstance.stream.on("error", (err) => { toast.error(`Connection to ${socketUrl} failed`); clientInstance.end(); clientInstance = null; // 出错后重置,方便后续重新连接 }); } return clientInstance; };
在onCacheEntryAdded中使用:
async onCacheEntryAdded( arg, { updateCachedData, cacheDataLoaded, cacheEntryRemoved } ) { try { const client = getMqttClient(socketUrl, options); // 传入对应地址和配置 await cacheDataLoaded; const handleMessage = (topic, data) => { const message = JSON.parse(data.toString()); updateCachedData((draft) => { if (!message.payload) return; draft.messages.unshift(message.payload); }); }; client.on("message", handleMessage); // 缓存移除时清理监听,避免内存泄漏 await cacheEntryRemoved; client.off("message", handleMessage); } catch (err) {} }
方案2:利用RTK Query的extra字段(推荐)
创建RTK Query的createApi时,通过extra参数传递MQTT客户端实例,这样能在onCacheEntryAdded中直接获取,完全符合RTK的设计规范。
实现示例:
// 创建API const inboxApi = createApi({ reducerPath: "inboxApi", baseQuery: fetchBaseQuery({ baseUrl: "/" }), endpoints: (builder) => ({ getMessages: builder.query({ query: () => "/messages", async onCacheEntryAdded( arg, { updateCachedData, cacheDataLoaded, cacheEntryRemoved, extra } ) { const client = extra.mqttClient; await cacheDataLoaded; const handleMessage = (topic, data) => { const message = JSON.parse(data.toString()); updateCachedData((draft) => { if (!message.payload) return; draft.messages.unshift(message.payload); }); }; client.on("message", handleMessage); await cacheEntryRemoved; client.off("message", handleMessage); }, }), }), }); // 创建Store时传入客户端实例 const client = MQTT(socketUrl, options); client.stream.on("error", (err) => { toast.error(`Connection to ${socketUrl} failed`); client.end(); }); const store = configureStore({ reducer: { [inboxApi.reducerPath]: inboxApi.reducer, inbox: inboxReducer, }, middleware: (getDefaultMiddleware) => getDefaultMiddleware({ thunk: { extraArgument: { mqttClient: client }, }, }).concat(inboxApi.middleware), });
方案3:禁用非序列化检查(不推荐)
如果只是临时调试,可以关闭Redux的非序列化检查,但这会丢失Redux状态可追踪的优势,不建议长期使用:
const store = configureStore({ reducer: { inbox: inboxReducer, }, middleware: (getDefaultMiddleware) => getDefaultMiddleware({ serializableCheck: false, }), });
内容的提问来源于stack exchange,提问作者Noman Baig
相关产品推荐
相关产品推荐

