React Native with RTK Query Pagination issue

63 views Asked by At

I have a React Native mobile application where I'm trying to manage API requests with RTK Query. On the 'Products' screen, I fetch and display data using getProducts (infinite scroll). On the displayed data, I have a button for deleting the product (deleteProduct). After executing the deleteProduct mutation, I want the deleted product to be removed from the screen. I attempted to use providesTags and invalidatesTags, but since there is a merge part in the query, I couldn't achieve this. Please help me.

export const productApi=baseApi.injectEndpoints({
    overrideExisting:true,
    endpoints : (builder) => ({
        getProducts: builder.query({
            query: (page=1) => `url?page=${page}`,
            transformResponse: (response) => {
                return response.data
            },
            serializeQueryArgs: ({ endpointName }) => {
                return endpointName
            },
            merge: (currentCache, newItems) => {
                currentCache.data.push(...newItems.data)
                currentCache.links = newItems?.links;
            },
            forceRefetch({ currentArg, previousArg }) {
                return currentArg !== previousArg
            },
            providesTags: (result) =>
                result
                    ? [
                        ...result.data.map(({ id }) => ({ type: 'Products', id })),
                    ]
                    : [{ type: 'Products', id: 'PARTIAL-LIST' }],
        }),

        deleteProduct: builder.mutation({
            query : (id) => ({
                url: "products/delete",
                method: "POST",
                body: {
                    id:id
                }
            }),
            invalidatesTags: (result, error, id) => [
                { type: 'Products', id },
            ],
        })
    })
})
1

There are 1 answers

2
Pelayo Martinez On

You can use updateQueryData inside onQueryStarted to manually update the cache entry for that file Check this out

export const productApi=baseApi.injectEndpoints({
  overrideExisting:true,
  endpoints : (builder) => ({
      getProducts: builder.query({
          query: (page=1) => `url?page=${page}`,
          transformResponse: (response) => {
              return response.data
          },
          serializeQueryArgs: ({ endpointName }) => {
              return endpointName
          },
          merge: (currentCache, newItems) => {
              currentCache.data.push(...newItems.data)
              currentCache.links = newItems?.links;
          },
          forceRefetch({ currentArg, previousArg }) {
              return currentArg !== previousArg
          },
          providesTags: (result) =>
              result
                  ? [
                      ...result.data.map(({ id }) => ({ type: 'Products', id })),
                  ]
                  : [{ type: 'Products', id: 'PARTIAL-LIST' }],
      }),

      deleteProduct: builder.mutation({
          query : (id) => ({
              url: "products/delete",
              method: "POST",
              body: {
                  id:id
              }
          }),
          onQueryStarted: async (arg, { dispatch, queryFulfilled }) => {
              const patchResult = dispatch(
                  productApi.util.updateQueryData('getProducts', (draft) => {
                      draft.data = draft.data.filter((res) => res.id !== arg);
                  }),
              );
              try {
                  await queryFulfilled;
              } catch {
                  patchResult.undo();
              }
          },
      })
  })
})

^ Do this instead of invalidateTags so you don't trigger a full reload

The only issue is that your pagination will now be off by one (the next page you load will actually skip one item). So you probably have to add an offset or something to compensate. I'm still trying to figure out myself how to deal with this