Closed strblr closed 3 years ago
When you invalidate you delete data from the cache; hence an optimistic mutation can never invalidate data eagerly and expect a cached response for this data.
Furthermore when the UI doesn't reflect the changes you've made then that means it's being forced to wait for the mutation to complete and it being able to send a request to the API because some of your data is missing.
Most recent discussion with info on this is this one: https://github.com/FormidableLabs/urql/discussions/1645
Edit: Also I spotted another small thing. Invalidating is an explicit call to delete and invalidate data, like is said, and I spotted that you expect project
to be temporarily set to null
. Two conditions are however preventing you from seeing this:
project
field in your example is actually a required field in your schema. This means that it isn't actually nullable, so even with schema awareness it wouldn't be set to null
@kitten
I'm not sure I understand all the implication of your explanation.
When you invalidate you delete data from the cache; hence an optimistic mutation can never invalidate data eagerly and expect a cached response for this data.
Actually, the data of my useQuery
(on the project
query) is not nullified in case of a single invalidation. And that's fortunate because otherwise how could one refetch outdated queries to repopulate the cache after a mutation without having loaders and spinners popping up all over the app ? Right now, if I call cache.invalidate
on my Project
one time after a mutation, useQuery
is refetched but still shows the old Project
(please don't change that haha, Apollo did in this unpopular PR and it was one of the many reasons I switched).
My Query.project
data is only nullified if the mutation is called two times too fast. I don't know if it's because the second mutation needs the first one to finish, or if the second mutation needs the query refetches triggered by the first one (after cache invalidation in updates
) to finish. Is it possible that the Query.project
data is forced to null
after two consecutive invalidations on the same entity ?
Furthermore when the UI doesn't reflect the changes you've made then that means it's being forced to wait for the mutation to complete and it being able to send a request to the API because some of your data is missing.
I'm trying to imagine what's going on here :
1) First mutation is called, thus optimistic.editContradiction
is called, the contradiction fragment is complete and the UI updates immediately.
2) The first mutation returns from the server, updates.Mutation.editContradiction
is called and invalidates a specific Project
.
3) This invalidation triggers the refetch of a Query.project
query.
4) While this refetch happens, the second mutation is called, followed by optimistic.editContradiction
.
5) While reading the contradiction fragment, the Contradiction.project
field is now missing (invalidated in 2.).
6) So the optimistic result is not applied and waits for the actual mutation to respond. Therefor, I lost the benefit of using optimistic results.
Does it seem like that's what's going on ? If so, what would be the solution ? Is there a way to repopulate the cache with new query data after a mutation without deleting cached data (simply overwriting it) ?
and I spotted that you expect project to be temporarily set to null
No, I actually expect project to never be null, as specified in the schema.
This means that it isn't actually nullable, so even with schema awareness it wouldn't be set to null
But why is it, then ?
urql version & exchanges: 2.0.1, graphcache 4.0.0
I'm having two issues with optimistic responses and data invalidation. I don't know if they are related. To set up the stage, imagine having the following very simplified schema :
A contradiction is part of a project, and can be edited via
Mutation.editContradiction
. A project can be retrieved viaQuery.project
. Let's add the following (also very simplified) Urql client :Here are my issues :
1) When I trigger
editContradiction
two times in a row, with the second one being triggered before the first had a chance to return from the server, the optimistic update is not applied for that second one. The UI is just frozen after that second trigger until the mutation actually completes.2) When invalidating the contradiction's
Project
inupdates
as shown in the code snippet above, the second trigger ofeditContradiction
causes myproject
data (from auseQuery
onQuery.project
) to be set tonull
for a brief period of time until it's refetched. This seems to happen after the first call ofeditContradiction
returned from the server although I'm not 100% sure. If I wait for eacheditContradiction
to finish and forQuery.project
to be called again as a result of the invalidation before calling anothereditContradiction
,Query.project
is never set tonull
. I absolutely don't know what's going on here.My workaround for the first issue is to store the
contradiction
state in a React state that can be updated immediately, and calleditContradiction
without an optimistic response as a side effect of those React state updates.But I don't have a workaround for the second issue, which currently breaks the UI (because
Query.user
being null not only violates the typing generated by codegen (which only allows for the actual data orundefined
), it also triggers the rendering of a big loader that unmounts the whole project UI). This is a problem for all users clicking fast on mutation buttons while having a slow internet.Thanks in advance for your help.