redux-toolkit icon indicating copy to clipboard operation
redux-toolkit copied to clipboard

Pessimistic Updates no triggering

Open rhercb opened this issue 3 years ago • 10 comments

I can't find issue of why my pessimistic update doesn't work. If im using optimistic updates, everything work correct, but not the case with pessimistic update. I followed documentation, checked with debugging that everything should work as expected, but can't figure out what i'm missing.

I'm trying to delete a task from list, so nothing over the board, just need to check for ID of deleted task and remove it from cache.

working optimistic update

onQueryStarted({ projectId, taskId }, { dispatch, queryFulfilled }) {
    const mutationResult = dispatch(
        tasksApiSlice.util.updateQueryData("getAllTasks", projectId, (draft) => {
            const index = draft.projects.data.map((el) => el.id).indexOf(taskId);
            if (index >= 0) {
                draft.projects.data.splice(index, 1);
            }
        })
    );

    queryFulfilled.catch(mutationResult.undo);
},

pessimistic update example

async onQueryStarted({ projectId, taskId }, { dispatch, queryFulfilled }) {
    try {
        await queryFulfilled;
        dispatch(
            tasksApiSlice.util.updateQueryData("getAllTasks", projectId, (draft) => {
                const index = draft.projects.data.map((el) => el.id).indexOf(taskId);
                if (index >= 0) {
                    draft.projects.data.splice(index, 1);
                }
            })
        );
    } catch {}
},

With pessimistic update, it goes like this

  1. API request is triggered
  2. I Get the response status: 200, so everything is ok
  3. Pessimistic update doesn't trigger, data is no removed from draft
  4. Tag invalidation triggers and only then everything shows.

rhercb avatar Jun 09 '22 07:06 rhercb

Have you tried to log after queryFulfilled or in the catch block to see what's going on?

phryneas avatar Jun 09 '22 07:06 phryneas

Yes, everything seemed fine, data was there after queryFulfilled and catch{} also seemed fine, got errors.

rhercb avatar Jun 09 '22 08:06 rhercb

Can you provide a CodeSandbox, repo, or Replay ( https://replay.io/record-bugs ) that reproduces the issue?

markerikson avatar Jun 29 '22 02:06 markerikson

Well, sorry about the late response, but i managed to figure out that problem was with invalidating Cache. When we create pessimistic updates, we are not supposed to invalidate cache, didn't quite get that from documentation.

But, there is another problem, that, after dispatching updates, they are created multiple times. Have not figured out how to solve that.

async onQueryStarted({ projectsTableData }, { dispatch, queryFulfilled }) {
  const { orderBy, orderDirection, perPage, currentPage, filterData} = projectsTableData;

  try {
      const { data: createdProject } = await queryFulfilled
      dispatch(
          projectsApiSlice.util.updateQueryData('getAllProjects', { orderBy, orderDirection, perPage, currentPage, filterData }, (draft) => {
              draft.projects.data.splice(0, 0, createdProject.project)
          })
      )
  } catch (err) {
      console.log(err)
  }
}

Is there a way how to tell that the cache has been updated with the new record and to stop updating?

rhercb avatar Jul 28 '22 08:07 rhercb

Ok, i guess since this is a async function, it triggers on every change, thats why projects started to show up in multiple counts.

My workaround was to check the draft of the cache, and if there is a project with my new projects id, then don't make changes.

const oldDraft = draft.messages.map(el => el.id)
if (!oldDraft.includes(createdProject.project.id)) {
    draft.projects.data.unshift(createdProject.project);
}

rhercb avatar Jul 28 '22 17:07 rhercb

I'm a bit irritated. What do you mean by "multiple times"? This will run once for every query of that endpoint made to the server.

phryneas avatar Jul 28 '22 19:07 phryneas

What do tou mean by “every query of that endpoint”? I had one query which cache i overwrite. How come that doing that created 7 updates to that cache? It will do cache update to certain endpoints based on amount of other endpoint queries?

example - i have update get, create and delete endpoints, i run pessimistic update in create query. It will do cache update 4 times, because i have 4 endpoints?

help me understand, and dont be irritated. Im a bit irritated that documentation provides very little information about these subjects. Thats why i need help of those who maintain package.

rhercb avatar Jul 28 '22 20:07 rhercb

@rhercb it would really help if you could provide a complete CodeSandbox showing what you're trying to do, and include some comments pointing to what you expect to happen and what isn't working. Trying to follow along from some of these isolated code snippets is really hard :(

markerikson avatar Jul 28 '22 20:07 markerikson

@markerikson i will try to provide sandbox with this problem in following days, so that we could fogure out what i was missing. I managed to figure out solution to my problem in that time, but still, would be nice to have a proper closure as to what i was missing.

rhercb avatar Jul 28 '22 20:07 rhercb

That's kinda my point - I don't think we can give an answer atm, because we don't yet even understand what you're trying to do :)

markerikson avatar Jul 28 '22 20:07 markerikson

for whoever may stumble upon the same problem when pessimistic updates do not refresh the UI: my problem was in invalidateTags

deleteMessage: builder.mutation<void, {messageId: MessageId, threadId: ThreadId}>({
            query: ({messageId, threadId}) => ({
                url: '/messages',
                method: 'DELETE',
                body: {messageId}
            }),
            async onQueryStarted({messageId, threadId}, { dispatch, queryFulfilled }) {
                await queryFulfilled
                dispatch(messagesApi.util.updateQueryData('getMessages', threadId, (draftMessages) => {
                    const deletingMessageIndex = draftMessages.findIndex(m => m.id === messageId);
                    if (deletingMessageIndex !== -1) {
                        draftMessages.splice(deletingMessageIndex, 1);
                    }
                }));
            },   
            // invalidatesTags: ['Messages']
        })

I suppose it is because UI doesn't show "pending" state, and when you have invalidateTags, the state will be pending until it receives the next update from the server.

cronon avatar Dec 09 '22 13:12 cronon