Search code examples
reactjssocket.iomultiplayerrtk-query

React RTK Query with Socket.io connection behaves strangely during data update


I'm making a basic multiplayer game using React with RTK Query and Socket.io. I followed the RTK Query documentation for subscription case where they made a chat app. Basically the only change I make is instead of appending the new data to the end of an array, I'd use a single object instead I'd like to update. But when I try to use an object, it stays {}.

The annoying thing is if I use the array solution it works just fine. (So just using array[0] as the draft)

So basically the only differencies are:

  1. initGame: builder.query<ClientGame[],... -> initGame: builder.query<ClientGame,...
  2. queryFn: () => ({ data: [] }) -> queryFn: () => ({ data: {} as ClientGame })
  3. draft[0] = newGameState -> draft = newGameState

Works fine:

export const menuApi = createApi({
    reducerPath: 'menuApi',
    baseQuery: fetchBaseQuery({
        baseUrl: '/'
    }),
    tagTypes: ['Menu'],
    endpoints: (builder) => ({
        initGame: builder.query<ClientGame[], void>({
            queryFn: () => ({ data: [] }),
            async onCacheEntryAdded(
                _, // arg
                { updateCachedData, cacheEntryRemoved }
            ) {

                const socket = getSocket();
                try {
                    socket.on(ServerEvents.InitGame, (newGameState: any) => {
                        updateCachedData((draft) => {
                            draft[0] = newGameState
                        })
                    })

Won't return with actual data:

export const menuApi = createApi({
    reducerPath: 'menuApi',
    baseQuery: fetchBaseQuery({
        baseUrl: '/'
    }),
    tagTypes: ['Menu'],
    endpoints: (builder) => ({
        initGame: builder.query<ClientGame, void>({
            queryFn: () => ({ data: {} as ClientGame }),
            async onCacheEntryAdded(
                _, // arg
                { updateCachedData, cacheEntryRemoved }
            ) {

                const socket = getSocket();
                try {
                    socket.on(ServerEvents.InitGame, (newGameState: any) => {
                        updateCachedData((draft) => {
                            draft = newGameState
                        })
                    })

Vs code even thinks that draft is unused Image from vscode displaying unused variable


Solution

  • Meanwhile I found the answer. Sending the recived data directly is the key.

    socket.on(ServerEvents.InitGame, (newGameState: ClientGame) => {
      updateCachedData(() => {
        return newGameState;
      })
    })
    
    // Or using the spread operator to update partially
    updateCachedData((draft) => {
      return { result: { ...draft.result, stage: newStage.result } }
    });