react-apollo-hooks
react-apollo-hooks copied to clipboard
Getting stale data from cache
I've got queries and mutations working as hooks, and the mutations are even updating the cache correctly. However, the components using the updated queries get a stale result on the next render and see the update in subsequent renders.
I have a simple list-detail set of components with one route showing all items and another route showing the selected item. This sequence of operations will show stale data for the item when it's loaded in a list but not when it's loaded by itself:
- load
/#/items
- click the fifth one to go to
/#/item/5
- activate a mutation on the item, e.g. a Disable button
- the item will correctly show as "disabled" (up-to-date when single)
- click the nav link to go back to
/#/items
- the fifth will still erroneously show as "enabled" (stale when in a list)
- click any item
- click the nav link to go back to
/#/items
again - the fifth will now correctly show as "disabled" (up-to-date on second viewing, even in a list)
Something similar happens when using client.resetStore()
and client.clearStore()
. Forcing an update on a visible component with a query gets the cached results without hitting the server. Navigating away and back so the component with the stale query data gets destroyed and recreated finally loads the data from the server.
I'm following all the examples I've found, but here's the relevant code just in case:
// List.js
export default function Items() {
const { data, error, loading } = useQuery(...);
... check loading and error ...
return <ul>
{data.items.map(item =>
<li><a href={`/#/item/${item.id}`}>{item.name}</a></li>
)};
</ul>;
}
// Detail.js
export default function Detail({ params: { match: { id } } }) {
const mutate = useMutation(...);
return <button onClick={mutate}>Disable</button>;
}
I'd greatly appreciate any help or pointers on how to track down this issue in case I'm doing something wrong. Everything else is working fine so I think I've set it all up correctly. Any thoughts?
we're seeing the exact same thing.
Triggering mutation and moving to a new page, at which point we call useQuery to get the new data, which is out of date at that point. By going back and forward again we can see that it gets the correct data.
do you use the skip
flag in the options to try to reload the data?
We have had the same issue when passing the same variables in!
It seems skip
is not part of the useMemo array that listens for changes: https://github.com/trojanowski/react-apollo-hooks/blob/master/src/useQuery.ts
A work around is:
const { data, error, loading } = useQuery(YOUR_QUERY, {
variables: {
id
},
metadata: !shouldLoadData, // the trick
skip: !shouldLoadData,
fetchPolicy: "no-cache"
});
metadata
is part of the changes that useMemo listens to.
I'm not using skip
or changing the fetchPolicy
. I assumed Apollo would forget any objects that it altered as the result of a mutation. It's smart enough to update its cache, but it needs a mechanism to update its memoized values as well. Or is the memoization part of the hooks instead of the client?
@dharkness careful that without skip
your query will run on every re-render.
The memoization is part of the custom hook useQuery
not the apollo client.
@otrebu Are you sure that's what skip
is for? I'm not seeing queries to my server on each rerender. A new query is sent only when the variables change.
It looks like you use it to skip the query when it would be invalid. In their example, they skip loading the "latest dog photo for a breed" query when there's no breed selected.
<Query
query={GET_DOG_PHOTO}
variables={{ breed }}
skip={!breed}
>
@dharkness not sure about anything :) There is no mention in the this repo docs. I probably should check the Apollo docs. I had issues in the past that my query was re-running when I didn't want it to. So I usually use it like this:
skip: !breed || dog
To not run it if I have the dog data, or if I don't have my variable ready yet.
I didn't have issues, till I had your issue of getting stale data.
Are you executing multiple queries in that component? In my case, I'm running one query which provides the data. Using skip
to skip the query would leave me with no data and nothing to render. With skip
set to true
, the returned data
should always be null
. That's what happens when I add it to my query.
From the Apollo docs:
skip
: boolean Ifskip
is true, the query will be skipped entirely.
This is different from using no-cache
or network-only
for `fetchPolicy' to bypass the cache.
This happens to me too. If I use render components it works
@dharkness In some places I might be running multiple queries, but in most I don't.
I use useState to store my data from the query. So I use the skip flag to skip if I already have data, or if I don't have the variable yet to query it by.
Yes the fetchPolicy
is something different. I just copied and pasted it my scenario above.
I might be triggering more re-renders that I need to, and that seems causing my data to be fetched many times without using skip. That is all. Regardless of the proper usage of skip, hope you tried my solution about stale data :) it worked for me.
Cheers
I have the same issue, only when navigating between pages after a mutation.
My use case is:
- List of items + link to a "/new-item" page with form
- Submit form (prepending new item to cache list in update of mutation) and navigate back to the list
- New item is not in the list
As mentioned above, if i navigate to another page and back, it appears. Or if i move that form inline to the item list, and submit it, it properly adds it to the cache and appears.
Skip doesnt sound like the right thing at all? is this perhaps a new bug, with react-apollo-hooks/react-apollo?
metadata: !shouldLoadData, // the trick
This was what made it work for me, not skip
.
Using metadata
is definitely a hack, like adding a fake query parameter to defeat the cache. You could also use fetchPolicy: 'network-only'
, but I don't want to be responsible for invalidating the cache when Apollo already does this for you.
AFAICT, there's a bug in the hook's memoization because the base components supposedly work fine.
setting errorPolicy to "ignore" is another hack that seems to work for me?
Setting errorPolicy
has no effect for me, and I'm no longer seeing the updated data at any point without reloading the page.
Using
metadata
is definitely a hack, like adding a fake query parameter to defeat the cache. You could also usefetchPolicy: 'network-only'
, but I don't want to be responsible for invalidating the cache when Apollo already does this for you.
It is a hack yes, till someone doesn't look further into it. I called it work around when I first suggested it.
In my case the request wasn't made at all, I tried changing the fetchPolicy, but it didn't work for me.
Same issue here, working totally fine if I replace the useQuery
with the Query
component from react-apollo. So definitely a react-apollo-hooks issue
I have this issue too. Appears the useQuery does not listen for cache updates. The following will not cause the "const result = useQuery" to re-render after a mutation.
const result = useQuery(FooGql);
const { query } = result;
const mutFoo = useMutation(FooGql);
const mutator = () =>
mutFoo({
update: (story, { data }) => {
//update logic
store.writeQuery({ query, data: newData });
}
});
The following will update the cache via the hook and thus the hook see's the update.
const result = useQuery(FooGql);
const { query, updateQuery } = result;
const mutator = () =>
mutFoo({
update: (story, { data }) => {
//update logic
updateQuery(() => newData);
}
});
Edit: Further investigation found a const array = useMemo not updating when a complex data structure had it's internal's updated without creating a new object. Please dis-regard this post.
I encountered this too and had to use the metadata hack to work around it. It seems like a pretty basic use case to query for something, edit it in a modal, update it, then expect the underlying page to show the updates. The Apollo cache is correct and I can't think of a scenario where anyone would want useQuery to return something different than is in the cache. Perhaps it could run with cache-only any time skip is false and use the default fetchPolicy only when the variables change.
The issue still exists:
"@apollo/react-hooks": "3.1.3"
"apollo-cache-inmemory": "1.6.3",
"apollo-client": "2.6.4",
Still exists. Moving to Query
component instead