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

存储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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 02:40:32