I am trying to use cache.modify
to update a field after mutation. This is reactions
field of my Message
model. The mutation removes reaction from the reactions
field. I am using optimistic response.
The strange thing is that update
is called with unmodified cache when it's triggered for the second time upon getting backend response.
Here is a small code sample (removeReactionMutation
is the mutation function returned by useMutation
hook):
const { data } = await removeReactionMutation({
variables,
optimisticResponse,
update: ((cache, options) => {
const { data } = options;
console.log('update called', options);
console.log(
'optimistic cache',
JSON.stringify(
cache.extract(true)[GUID2ACID(variables.messageId)].reactions
)
);
console.log(
'real cache',
JSON.stringify(
cache.extract()[GUID2ACID(variables.messageId)].reactions
)
);
cache.modify({
id: GUID2ACID(variables.messageId),
fields: {
reactions(existing) {
console.log('removing reaction', {
existing,
userId: identity.guid,
emojiId: variables.emojiId,
});
return existing.filter(
(reaction: IAPIMessageReaction) =>
reaction.userId !== identity.guid ||
reaction.emojiId !== variables.emojiId
);
},
},
optimistic: true, // this flag seems to have no effect
});
console.log(
'optimistic cache',
JSON.stringify(
cache.extract(true)[GUID2ACID(variables.messageId)].reactions
)
);
console.log(
'real cache',
JSON.stringify(
cache.extract()[GUID2ACID(variables.messageId)].reactions
)
);
}),
});
Here is the console.log
output:
useEmojis.ts:163 update called {data: {…}} <----- called with optimistic response
useEmojis.ts:164 optimistic cache [... 5 records]
useEmojis.ts:170 real cache [...5 records]
useEmojis.ts:195 removing reaction {existing: Array(5), userId: '...', emojiId: '...'}
useEmojis.ts:210 optimistic cache [...4 records]
useEmojis.ts:216 real cache [...4 records]
useEmojis.ts:163 update called {data: {…}} <----- called with backend response
useEmojis.ts:164 optimistic cache [... 5 records] <----- WHY!?
useEmojis.ts:170 real cache [... 5 records] <----- WHY!?
useEmojis.ts:195 removing reaction {existing: Array(5), userId: '...', emojiId: '...'}
useEmojis.ts:210 optimistic cache [... 4 records]
useEmojis.ts:216 real cache [... 4 records]
So during the first update
call the item was removed from cache by cache.modify
(both optimistic and main layers). UI is correctly updated and reaction is removed. So far so good!
However, the item still exists in cache and the existing
value when update
callback is called second time after getting server response!
This is super confusing and it causes problems.
In case update
is quickly called multiple times and then it gets called multiple times again as server responses are coming in one by one, the outdated cache and existing
value cause the removed items to re-appear for a moment in the UI before they finally get removed.
This is because you have two pending cache.modify
calls. Both calls remove its own item but since the first call gets existing value with the other item wrongly present in the array, this very item will re-appear for a moment in the UI after the first cache.modify
call. The first call won't remove it (which is expected) and the other item shows up until second call finally removes it. I hope this makes sense.
So in the end you need some external logic to hold all items that should be removed and filter existing
so that all items that are about to be removed are excluded from existing
value. The need to have an external logic like this doesn't feel right.
Also, I tried calling cache.modify
with optimistic
flag On/Off but in either cases both cache layers are modified so it's unclear what this flag is about.
My questions is why during the second call of the update
callback, cache again holds the deleted item in both layers?
In other words, cache is in unmodified state during second update
calls and that's not what is rendered in the UI atm. This is something I can't understand and I'd appreciate any help or thoughts here.
Ok, I figured it out...
There is no mystery and the cache gets updated because of real-time subscriptions. So it happens outside of the mutation update
callback. Between callback runs, cache is modified due to real-time subscription message coming in.
I'll just keep this answer for anyone who might bump into a similar problem. Most likely you need to look for other places where cache can be updated.