Intended outcome:
My motivation is to invalidate data in cache, so that queries can refetch invalidated data.
(_For that reason I just return _id
from mutation. I know I can return data with mutation, but this scenario has implication on invalidating data on mutations with side effects._)
For that purpose I'm using cache.modify
and INVALIDATE flag to invalidate data in cache based on docs.
My expectation is that invalidation will result in query refetching data from server because data is not valid any more.
I also tried evict
and it works, but my impression is that invalidate
mechanism should allow us to refetch data based on dirty status. Did I miss something in code or did I misunderstood it? Thanks!
Actual outcome:
Query that fetches all todos is reobserved but it doesn't refetch data.
I tried to debug it and it seems cache diff is non existent. After cache.modify invalidation, query is reobserved but data is resolved from cache instead refetched.
What I found is that INVALIDATE is translated into dirty
status here, but Query when calculating diff doesn't see any difference here
How to reproduce the issue:
Open Codesandbox and try to change status of todo item.
It doesn't change.
Versions
System:
OS: macOS 10.15.5
Binaries:
Node: 14.2.0 - ~/.nvm/versions/node/v14.2.0/bin/node
Yarn: 1.22.5 - ~/.yvm/versions/v1.22.5/bin/yarn
npm: 6.14.4 - ~/.nvm/versions/node/v14.2.0/bin/npm
Browsers:
Chrome: 85.0.4183.121
Firefox: 75.0
Safari: 13.1.1
npmPackages:
@apollo/client: 3.2.0 => 3.2.0
@vedrani You're right, INVALIDATE
by itself isn't the best fit with a cache-first
policy, since the invalidated data remain in the cache, so the next cache read usually succeeds, so no network request happens.
To make the most of INVALIDATE
, you need to have some control over how queries respond to the invalidation signal. If you're doing a mutation, refetchQueries
can help, but I am also currently working on a new mutation option that I think can take the place of refetchQueries
in most cases:
export default function Todo({ todo }) {
const [updateTodo] = useMutation(UPDATE_TODO, {
update(cache, { data: { updateTodo } }) {
cache.modify({
id: cache.identify(updateTodo),
fields(fieldValue, details) {
return details.INVALIDATE;
}
});
},
// This callback will be called with each ObservableQuery that was
// affected by the mutation update function, so you can decide how
// to refetch the query. The default behavior is the same as calling
// observableQuery.reobserve(), but calling refetch instead will force
// a network request.
reobserveQuery(observableQuery) {
return observableQuery.refetch();
},
});
I'll let you know when that's ready for testing (or you can watch #7015 for upcoming betas).
Thanks for trying out the INVALIDATE
feature in the meantime, and for providing such thoughtful feedback!
@benjamn I'm also interested are we going to have utility that will allow us to just watch dirty status in cache for some query? Or query callback will be enough?
What is interesting for me is case of expensive or slow query. Will I have opportunity just to show for example Reload button instead of refetching query immediately?
Thanks!
@benjamn what about queries that are not currently active?
I mean, I have 2 pages, in page 1 I have a list of items, in page 2 I have a function where I can query some data or do something and I need to invalidate data from page 1. Then when I navigate to page 1, I want to show existing data but since cache data is invalidated, I want to refetch data form the server. The fetch policy in page 1 query is cache first
The way I see INVALIDATE or how I would like to use it in this case is whenever I go to page 1, if data is valid and fetchPolicy is cache-first
, then no network request is made, but if somewhere if my app I invalidate the data for a particular fieldName, next time, even with cache-first, a network request should be made and cached (invalidated) data should be returned before the request is made.
@benjamn I've tried invalidating a field using "@apollo/client": "3.3.4"
and the following based on the above suggestion. thread query still appears to be loading from the cache and doesn't reflect the new post. Shows up fine after reloading the page.
const mutation = useMutation({
mutation: CreatePost,
update (cache, {data: {createPost: post}}) {
const {thread_id} = post;
cache.modify({
id: `Thread:${thread_id}`,
fields: {
posts: (_, details)=> details.INVALIDATE
}
});
},
reobserveQuery (query) {
return query.refetch();
}
});
similar situation when i try substituing the following
cache.modify({
id: `Thread:${thread_id}`,
fields: (_, details)=> details.INVALIDATE
});
I've also tried the following as part of my apollo client config as mentioned here without any luck there either
query: {
fetchPolicy: 'cache-and-network',
nextFetchPolicy: 'cache-first'
}
Probably worth adding that this field has a typePolicy for pagination associated with it in case that has some bearing on implementation bugs.
I have to say I've been continually gotcha'd by the over-engineering in Apollo client. The semantics of INVALIDATE should probably be as simple as possible. If I invalidate cache data, no matter what the fetchPolicy is, any subsequent requests to that field (or fields) in the cache should miss and issue a network request.
I have to say I've been continually gotcha'd by the over-engineering in Apollo client. The semantics of INVALIDATE should probably be as simple as possible. If I invalidate cache data, no matter what the fetchPolicy is, any subsequent requests to that field (or fields) in the cache should miss and issue a network request.
Definitely agreed. I spent an unfortunate amount of time today misunderstanding INVALIDATE semantics.
@benjamn If INVALIDATE is not meant to trigger a refetch, what is the correct way to indicate to Apollo client that a specific field is invalid and should trigger a refetch?
Most helpful comment
@vedrani You're right,
INVALIDATE
by itself isn't the best fit with acache-first
policy, since the invalidated data remain in the cache, so the next cache read usually succeeds, so no network request happens.To make the most of
INVALIDATE
, you need to have some control over how queries respond to the invalidation signal. If you're doing a mutation,refetchQueries
can help, but I am also currently working on a new mutation option that I think can take the place ofrefetchQueries
in most cases:I'll let you know when that's ready for testing (or you can watch #7015 for upcoming betas).
Thanks for trying out the
INVALIDATE
feature in the meantime, and for providing such thoughtful feedback!