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

React中RTK Query结合Socket.io更新单对象数据异常问题

RTK Query订阅更新单对象时数据为空的问题解决

问题背景

用React、RTK Query和Socket.io开发多人游戏,参考RTK Query文档的聊天订阅案例,把原案例的数组追加逻辑改成更新单对象后,数据一直是空对象{};但用数组方案(把对象存在draft[0]里)能正常运行。两者差异点:

  • 泛型从<ClientGame[]>改为<ClientGame>
  • queryFn返回数据从[]改为{} as ClientGame
  • updateCachedData里从draft[0] = newGameState改成draft = newGameState
    VS Code提示draft未被使用。

问题原因

RTK Query的updateCachedData依赖Immer处理不可变更新,Immer的核心逻辑是修改draft代理对象的属性来生成新状态,而非直接给draft变量重新赋值。直接写draft = newGameState只是改变了局部变量的引用,根本没触碰到Immer追踪的代理对象,所以缓存不会更新,最终数据还是初始的空对象。

而数组方案里的draft[0] = newGameState是修改数组draft的元素,属于Immer允许的操作,因此能正常更新缓存。

解决方案

要维护单对象缓存,不能直接给draft赋值,可用以下两种方式修改:

方案1:用Object.assign合并属性

把新对象的属性合并到draft中,适合浅结构的对象:

const gameApi = createApi({
  reducerPath: 'gameApi',
  baseQuery: fakeBaseQuery(),
  endpoints: (builder) => ({
    subscribeToGameState: builder.subscription<ClientGame>({
      queryFn: () => ({ data: {} as ClientGame }),
      async subscribe(listener, api, extraOptions) {
        const socket = io('/game');
        socket.on('gameState', (newGameState) => {
          api.dispatch(
            gameApi.util.updateCachedData(
              'subscribeToGameState',
              (draft) => {
                // 合并新状态到draft,触发Immer更新
                Object.assign(draft, newGameState);
              }
            )
          );
        });
        return () => socket.close();
      },
    }),
  }),
});

方案2:直接返回新状态对象

Immer支持在更新函数里直接返回新的状态对象,替换原缓存:

const gameApi = createApi({
  reducerPath: 'gameApi',
  baseQuery: fakeBaseQuery(),
  endpoints: (builder) => ({
    subscribeToGameState: builder.subscription<ClientGame>({
      queryFn: () => ({ data: {} as ClientGame }),
      async subscribe(listener, api, extraOptions) {
        const socket = io('/game');
        socket.on('gameState', (newGameState) => {
          api.dispatch(
            gameApi.util.updateCachedData(
              'subscribeToGameState',
              () => {
                // 直接返回新状态,Immer会处理替换逻辑
                return newGameState;
              }
            )
          );
        });
        return () => socket.close();
      },
    }),
  }),
});

补充说明

如果ClientGame是嵌套较深的对象,Object.assign只能浅合并,此时可以用Immer的嵌套修改(比如draft.someNestedProp = newNestedValue),或者引入深度合并工具(比如lodash的merge)来处理。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.30 13:18:13