rtk-incubator / rtk-query

Data fetching and caching addon for Redux Toolkit
https://redux-toolkit.js.org/rtk-query/overview
MIT License
626 stars 31 forks source link

Optimistic Update for deleting/duplicating item in Array #166

Closed cufta22 closed 3 years ago

cufta22 commented 3 years ago

I've setup everything and it works except you can see the small delay when deleting/duplicating items ( it doesn't feel live ), any idea how to solve this with onStart, this is my code:

export const api = createApi({
  reducerPath: "api",
  baseQuery: fetchBaseQuery({ baseUrl: "http://localhost:8081" }),
  entityTypes: ["Positions"],
  endpoints: (build) => ({
    getPositionsByCompany: build.query<Array<any>, void>({
      query: () => ({
        url: "/positions",
        method: "GET",
        credentials: "include",
      }),
      provides: (result: any) => [
        ...result.positions.map(({ _id }: any) => ({
          type: "Positions",
          id: _id,
        })),
        { type: "Positions", id: "LIST" },
      ],
    }),
    deletePosition: build.mutation<void, any>({
      query: (id: string) => ({
        url: `/positions/${id}`,
        method: "DELETE", 
        credentials: "include",
      }),
      onStart(id, { dispatch, context }) {
        //...
      },
      invalidates: (_, id) => [{ type: "Positions", id }],
    }),
    duplicatePosition: build.mutation({
      query: (position: { positionid: any; name: string }) => ({
        url: "/positions/duplicate",
        method: "POST", 
        body: position,
        credentials: "include",
      }),
      onStart(id, { dispatch, context }) {
        //...
      },
      invalidates: [{ type: "Positions", id: "LIST" }],
    }),
  }),
});

Below is example given in docs, it shows updating item, I've tried something similar with no success

onStart({ id, ...patch }, { dispatch, context }) {
    // When we start the request, just immediately update the cache
        context.undoPost = dispatch(
        api.util.updateQueryResult('getPost', id, (draft) => {
            Object.assign(draft, patch);
        })
    ).inversePatches;
},
phryneas commented 3 years ago

You can do anything in that updateQueryResult, so you could do

api.util.updateQueryResult('getPositionsByCompany', id, (draft) => {
            draft.push(draft[id])
        })

or something similar

cufta22 commented 3 years ago

You can do anything in that updateQueryResult, so you could do

api.util.updateQueryResult('getPositionsByCompany', id, (draft) => {
            draft.push(draft[id])
        })

or something similar

Had no luck with this sadly, my console log is not logging anything for draft and there's no action dispatched on my redux dev tools for this ( all other actions are there, queries/mutations ) so is there any other way to test this. I've tried other things on draft not just .push but nothing seems to work. Ik that docs are not yet 100% completed but what exactly are context, draft and updateQueryResult This is basically what I have, didn't want to copy whole thing again just the endpoint I worked on, I also noticed for Object.assign example Immer Patch is used so is this necessary here

deletePosition: build.mutation<void, any, any>({
      query: (id: string) => ({
        url: `/positions/${id}`,
        method: "DELETE", // post, put, delete...
        credentials: "include",
      }),
      onStart(id, { dispatch, context }) {
        context.undoPost = dispatch(
          positionsApi.util.updateQueryResult(
            "getPositionsByCompany",
            id,
            (draft) => {
              console.log(draft);
              draft.push(draft[id]);
            }
          )
        ).inversePatches;
      },
      invalidates: (_, id) => [{ type: "Positions", id }],
    }),
phryneas commented 3 years ago

draft is the current cache value that was returned by your "getPositionsByCompany" request that you called with the parameter id. It should implicitly be typed as MaybeDraft<Array<any>>, where Array<any> is the result value of your getPositionsByCompany query. You can just modify that as you would do in a normal immerized RTK reducer. Logging should be possible with console.log(current(draft)).

If there is no value for useGetPositionsByCompany(id) in the cache, that callback will not be called and nothing will be updated (since there is nothing to update).

Oh, and positionsApi.util.updateQueryResult is a thunk, so it will not be visible in the devtools unless it actually dispatches an action in the end - which I guess is just not happening because there was maybe no value in the cache with that id to be updated?

context is just a variable that is shared between onStart, onSuccess and onError for any given request, so you can use it to transport values from one to another.