I am using the "cache-and-network" policy.
Intended outcome:
Rerender Query with different variables. Data is {} until new request is resolved from cache or network.
Actual outcome:
Rerender Query with different variables. Returns previous data until next query is resolved without error.
How to reproduce the issue:
https://stackblitz.com/edit/react-snlwzf
change the input and hit submit.
Version
react-apollo@
[x] has-reproduction
Backgroud
I only want to display a loading state if I don't have a response from the cache or network, but I do want to show loading state when variables change and I don't have a response from cache. Right now it is impossible to tell if data is the response from cache or if it is the previous result. Loading property is no help because it is allows true until the network request is resolved.
Yeah, you basically have to use something like this to prevent rendering data from a query with different variables:
if (data.reddit && data.reddit.subreddit.name !== this.state.search) {
data = {};
}
And that only works if the data being returned also includes the variable you passed in. Definitely seems like a bug.
This is a pretty common issue. You need to do a check for equality of a
name/id and show a loading component or pass an empty/default obj if you
don’t want to see stale data. I would advise modifying the data variable,
if that’s the same one from the query result obj. Just use a ternary or
if/else. You may need to abstract it to an external component to ensure you
get rerenders reliably.
On Wed, Jul 25, 2018 at 10:45 AM James Wyatt Cready-Pyle <
[email protected]> wrote:
Yeah, you basically have to use something like this to prevent rendering
data from a query with different variables:if (data.reddit && data.reddit.subreddit.name !== this.state.search) { data = {};
}And that only works if the data being returned also includes the variable
you passed in. Definitely seems like a bug.—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
https://github.com/apollographql/react-apollo/issues/2202#issuecomment-407820510,
or mute the thread
https://github.com/notifications/unsubscribe-auth/AIM6lxQm220hAw84gsU2XQj043KYvC17ks5uKKCzgaJpZM4VZucg
.
@sbrichardson the problem is that OP wants to see cached data for query when the variable is the _same_, but the problem is that Apollo is displaying the cached results for wrong query if the cached results for the current query do not already exist in the cache.
The expected behavior is for Apollo to provide an empty data object when the results for the current query do not exist in the cache.
The actual behavior is that Apollo is providing the _previous_ data object when the results for the current query do not exist in the cache.
Please see a more detailed example here.
I do think that it can be valuable to show the previous result while the new one is loading. I have a search auto complete feature that does this. But I also need this case. I can't check for equality of a name or id because the query is a search query. The variables that are passed into the function child are the same as the current input so I can't check equality using that.
@jcready - thanks for the clear description. I suppose that both behaviors could be desirable in different situations, and it’s a matter of which is default.
With the suggested strategy it would be pretty pretty trivial to restore the current behavior by creating a component stores the Query’s variables/results in state and only updates them once the variables have changed and data is present.
Changing the existing behavior in userland is a bit trickier, given that we don’t know which variables the current data is from.
I agree that this is a bug.
This issue also looks to be duplicated in #1342 and #1963
I believe the source of this issue is around this bit of logic in the <Query/> component: https://github.com/apollographql/react-apollo/blob/2788981c300421e26d2522e5776c90d6aaa19f75/src/Query.tsx#L391-L400
Specifically line 392. Since a query with different variables will not have cached data, but will have previous data from the previous query with different variables, the data will end up being just the previous data. I would argue that solution is to reset this.previousData in the componentWillReceiveProps() method when shallowEqual(nextProps.variables, this.props.variables) is not true.
To support pagination perhaps it is necessary to have the component accept some new prop which would either accept an array of variable keys which would invalidate the previousData if changed or perhaps just accept a function which would act similar to a react component's shouldComponentUpdate(). I imagine something like:
<Query
query={query}
variables={variables}
fetchPolicy="cache-and-network"
shouldInvalidatePreviousData={(nextVariables, previousVariables) =>
nextVariables.subreddit !== previousVariables.subreddit
}/>
I agree that shouldInvalidatePreviousData should be a function that defaults to shallow equal. I implemented a simple replacement of the Query component that used that strategy and it worked well.
I've just stumbled across this bug. No news if this is going to be fixed?
This is very weird. I'm almost sure I've seen this working well before in another app, but I'm facing this now too. It is most likely a matter of how the query is being cached. The cache's PK needs to be the query plus variables.
Looking at the cache I do see different entries for different variable sets:

first discovery here: if you use refetch instead of changing the props from the parent, the networkStatus (https://www.apollographql.com/docs/react/api/react-apollo.html#graphql-query-data-networkStatus) will be 2 that's more appropriate than 1. If you pass from the parent it will be one.
I agree with @jcready. That's probably the error due to the behavior I've observed here.
I spent a lot of hours yesterday trying to find a workaround and this is the best I could come up with. I prefer using the graphql hocs approach together with recompose functions. Please adjust the solution to your scenario.
The best way I could find to show correct data was to check if the query really has a cache and then to infer that the data is from the cache instead of from the previous variable set.
I created this method to check for the cache:
const queryCacheExists = (client, query, variables) => {
try {
client.readQuery({
query,
variables,
});
return true;
} catch (e) {
return false;
}
};
And then this logic:
const hasItems = props.queryData.queryField ? props.queryData.queryField.edges.length > 0 : false;
const queryLoading = props.queryData.loading;
const showingCache = (
hasItems
&& queryLoading
&& queryCacheExists(props.client, MY_QUERY, props.queryData.variables)
);
So, after that, showingCache will indicate if the query is showing cached items. And then I can use a custom loading logic like:
loading: queryLoading && !showingCache,
If someone has any simpler solution, please let us know here on this issue.
I suspect I have the same problem, but with Mutation. I posted it here.
Encountered the same issue.
Solution:
shouldInvalidatePreviousData can be a generic solutionpreviousData in the loading phase when fetchPolicy contains use of cache, because that's a double useless caching layer, with side effect as we can see here.Relevent code:
https://github.com/apollographql/react-apollo/blob/386b6d7455b7fc6da7cea20afa152039c535dfe5/src/Query.tsx#L396-L402
Is there any work around for this or plans to fix it?
any update on this issue, this is a pretty big problem imo
I also encountered this problem using HOCs. It only starts to work properly when fetchPolicy is set to 'no-cache'. Any update on this?
Just bumped into this problem. It seems as if Apollo looks for a similar object in the cache and uses that without checking any variables. When cache persistence is enabled, even refreshing doesn't affect it, and the only way around it is to disable cache for the query, which is unacceptable when offline functionality is desired.
I'd assume the client making a new query when variables changed and data for them isn't in the cache is a sane default.
Encountering this, myself. I'll just have to turn off the cache for now, which is a bummer of a workaround.
Also encountering this issue.
I appreciate all the work done by the Apollo team but for me this is a very significant issue.
I faced same issue and I put fetchPolicy: 'no-cache', that saved me after hours of debugging my React App.
// Fetching new data for the PO Entity
let pageObjectQueryResult = await client.query({
query: getPageObjectEntityQuery,
variables: {
name: event.value
},
fetchPolicy: 'no-cache'
});
@TuHuynhVan that's a workaround which won't work for everyone. In my case, being able to cache queries is critical as I'm working on a PWA with offline functionality.
react-apollo 2.5.2 has a bug in it that prevents certain Query component updates from happening. This issue has been fixed in https://github.com/apollographql/react-apollo/pull/2840, and will be published soon (hopefully tonight / tomorrow). I have a feeling this will help fix some of the issues people have been encountering here.
@hwillson #2840 will not solve the problem.
@jcready It won't solve this entire issue thread, I agree. But I have a feeling it will help address some of the comments posted here in the past 2 weeks.
I've recently been bitten by this as well. In particular, when navigating from a "good" query to a query that has errors, the "good" data stays alongside the new errors, with no easy way to differentiate between the two. Disabling caching removes one of the main advantages of Apollo client.
I've been able to hack around this in a simple component by using the key property on the Query component and setting it to a primary key I use in my query:
<Query
key={this.state.id}
variables={{ customerId: this.state.id }}
query={GET_CUSTOMER}>
{() => <div>Hi</div>}
</Query>
This allows me to re-render "cleanly" from a good state to an error state then back, only dispatching an XHR for the "bad" state. Obviously does not scale well and is easy easy to overlook.
I'm not sure I understand the nuances of the issue but It seems like the behavior of doing a shallow comparison of variables before retrieving from cache makes the most sense. I'd prefer that over having to implement something myself (ala https://github.com/apollographql/react-apollo/pull/2889) all over my application.
Perhaps we could default to a shallow check and then allow users to override if the want different behavior?
@NickTomlin I've amended my PR (#2889) to default to doing a shallow comparison of variables as I agree that should be the default behavior.
I have been bitten by this as well. I am using react-apollo 2.5.4 and I am finding that after a query returns (I am using the graphql HoC), the resulting data object has the variables from the second query but the data from the first query.
I've been trying to find a workaround with no luck. @NickTomlin 's idea of using a key does fix the issue, but the key forcing the entire tree to re-render causes other problems.
Quick question - would people prefer to see something like the shouldInvalidatePreviousData prop / variable comparison function approach mentioned above, or would a simple clearPreviousDataOnLoad prop, that accepts a boolean (allowing you to do whatever comparisons you want outside of the Query component, and just pass the resulting boolean in) be good enough? I have a feeling most people will either want the previous data or not, so a simple on/off flag should help address most cases, but let me know (we can get this fixed in the next react-apollo patch release).
Boolean variable is enough for our usage. If we could turn off this behavior globally that would be great. I wouldn't want this error prone behavior trip up others in our codebase.
Thanks @chengyin - turning it off globally is a bit tricky with current day react-apollo, as there isn't really anywhere to configure this behavior as a global option, outside of HOC/component props. I wish we could just make the use of previous data something that needs to be enabled (not the default); that would probably wipe out about half of this repo's open issues 🙂. Unfortunately, that would be a pretty massive breaking change (possibly even too massive for react-apollo 3).
@city41 brought up another interesting option outside of this thread; what if we leave the behavior as is, but add an additional param in the response that lets people know the data coming back is previous data - something like isPreviousData: true. That might be an easier option than having to remember to set a clearPreviousDataOnLoad prop.
@hwillson what about adding a way to attach implementation-specific config onto the Apollo client itself so that it can be passed to components from there?
@DoMiNeLa10 Ideally we'd like to avoid adding React Apollo specific config elements to Apollo Client, since Apollo Client can be used completely independently of RA. One place we could consider setting config options is via the ApolloProvider functional component, to get them into the ApolloContext (which is a React Context instance). We could then check for common config items in the Context first, then as props on individual components second (like we do with the client instance). I've made a note of this and will definitely put some more thought into it as we progress on 3.0.
The above being said though, I'd love to get something in place to address this overall issue now (that we can keep in RA 3). I'm leaning more towards the isPreviousData: true / false approach, since that will get added into the query result coming back from Apollo Client, and RA 3's integration with Apollo Client is going to change the least out of everything else. So we can adopt it now, and it won't change. As an added bonus, if we decided to change the default behavior of RA such that previous data is never re-used without being opted into, having an isPreviousData property available in the query result still makes sense (less future changes === more 🏖).
We want to use _cached_ data, but we don't want to use cached data from a different query (e.g. different variables). So your boolean "isPreviousData" doesn't solve the problem unless that flag is set based on whether or not the variables changed.
@jcready that’s exactly what I’m envisioning. isPreviousData would really be more like isOnlyPreviousData; it would be set to true when only previous data is being returned as data.
When the variables are changed, currentResult.data in the above is an empty object. We would set isPreviousData in the response to true if this.previousData has data and currentResult.data is an empty object. So looking at the OP:
I only want to display a loading state if I don't have a response from the cache or network,
In this case, loading will be true and data will be empty.
but I do want to show loading state when variables change and I don't have a response from cache.
In this case, loading will be true and isPreviousData will be true.
So you could show loading state if loading is true and (data is empty OR isPreviousData is true).
how are things looking for isPreviousData, @hwillson ?
Perhaps obvious to most of you, but I had the same problem, with this code:
compose(
graphql(myQuery1, {
name: 'data',
}),
graphql(myQuery2, {
name: 'data',
}),
)
Once I gave the queries each a unique 'name' property, my problem was solved. I would assume it's because Apollo does that shallow object comparison, and figures that because 'data' exists in both results, the object is the same and therefore React's props shouldn't be updated.
I am hopping this issue will be resolved soon, crossing my fingers...
I've just read through this thread and the issue I'm having seems to be related...perhaps? Does <Query> do some object reference comparision on it's variables prop? What I'm seeing is this:
jsx
<Query query={query} variables={variables} fetchPolicy="network-only">
When I change the variables, it re-renders but then passes an empty {} to the data arg of the render function (and it shouldn't there's lots of data to load). And it makes NO network request despite network-only but still fires the onCompleted event despite not actually having done anything and emptying the data arg that was previously populated.
jsx
<Query query={query} variables={JSON.parse(JSON.stringify(variables))} fetchPolicy="network-only">
This works as expected making a network request on every render.
I'd be ok with it making a shallow comparison of variables but why render with data empty and not the previous result? Especially with network-only policy? Am I missing something?
Hi,
I had a similar issue as @nolandg and fix with the key that @NickTomlin provided helped. Elegant fix.
I have a query that request page data based on a variable (page URL path). It works with the simple setup but at the moment when I wanted to use the onError function as fall back to load 404 Page, all the problems started. Quite similar to what people mentioned before. Even if the variable changed, data loading and error variables would return incorrect values. Sometimes it stuck loading, other time it would always return empty data.
Thx @NickTomlin
Any updates on this? I feel like @jcready 's PR should be the default behaviour.
We were able to get around this in our use-case by checking if setVariables is set.
import { NetworkStatus } from 'apollo-client';
props: ({ Query, ownProps }) => {
const { user } = ownProps
const { loading, networkStatus, fields } = Query
const canTransform = user && fields && networkStatus !== NetworkStatus.setVariables;
return {
data: canTransform && transformData({ user, fields })
}
}
P.S. Hi @jcready (former coworkers) :)
We were able to get around this in our use-case by checking if
setVariablesis set.import { NetworkStatus } from 'apollo-client'; props: ({ Query, ownProps }) => { const { user } = ownProps const { loading, networkStatus, fields } = Query const canTransform = user && fields && networkStatus !== NetworkStatus.setVariables; return { data: canTransform && transformData({ user, fields }) } }P.S. Hi @jcready (former coworkers) :)
Even though there may be another solution embedded in Apollo, I think this is still a solid solution!
Thanks!
React Apollo has been refactored to use React Hooks behind the scenes for everything, which means a lot has changed since this issue was opened (and a lot of outstanding issues have been resolved). We'll close this issue off, but please let us know if you're still encountering this problem using React Apollo >= 3.1.0. Thanks!
@hwillson,
Version 3.1.0 seems to still have the same behavior.
To me we already have three solid approaches on how to solve this problem:
The first approach is my favorite because is more of a declarative/reacty way of doing things.
I guess the first two approaches would attend most of the use cases, but it does not invalidate the use cases solved by the third and I think after implementing one approach the other two would be trivial (guessing out of complete ignorance sorry if it is harder than it seems). The hooks version could have an array containing values that would cause the hook to clearPreviousData, it would work like the hooks version of the third approach.
Just brainstorming here to let you all know that this problem is still a thing and to get the discussion running again.
@hwillson, I am experiencing this problem when using useQuery in version 3.1.1 as a part of a Next.js app. I'm a new Apollo user and this is the first issue I've had with this excellent software.
useQuery fetches results using variables {"currency": "INR"}{"currency": "CAD"}useQuery correctly fetches and returns results using new variables{"currency": "INR"}useQuery incorrectly returns results as if variables were still {"currency": "CAD"}Setting fetchPolicy: 'no-cache' as a useQuery option fixes it. Let me know if I can provide any more info.
@hwillson, my problem was actually the result of me not understanding how InMemoryCache works. There is no bug.
The {"currency": "INR"} in my comment above was actually part of a larger structure that looks like this:
{
"search": [
{
"id": 321,
"mpn": "NE555",
"median_price_1000": {
"currency": "INR",
"price": 0.123,
"__type": "PricePoint"
},
"__type": "Part"
},
{
"id": 654,
"mpn": "NE555P",
"median_price_1000": {
"currency": "INR",
"price": 0.456,
"__type": "PricePoint"
},
"__type": "Part"
},
{
"id": 987,
"mpn": "NE555R",
"median_price_1000": {
"currency": "INR",
"price": 0.789,
"__type": "PricePoint"
},
"__type": "Part"
}
]
}
This over-simplified example was hiding the real cause of the problem. PricePoint doesn't have an ID, and so InMemoryCache was storing only the most-previously-fetched version of median_price_1000 for each Part as a part of its normalization process. It was treating all of a Part's PricePoint as being the same.
For anyone else who may be experiencing similar issues, by passing a dataIdFromObject function to InMemoryCache when initializing ApolloClient, I was able to append median_price_1000.currency to each Part's key to differentiate Part in the cache:
import withApollo from 'next-with-apollo'
import ApolloClient, { InMemoryCache, defaultDataIdFromObject } from 'apollo-boost'
export default withApollo(
({ initialState }) =>
new ApolloClient({
cache: new InMemoryCache({
dataIdFromObject: (o: any) => {
const id = defaultDataIdFromObject(o)
if (o.__typename !== 'Part' || id === null) {
return id
}
if (o.median_price_1000 === null) {
return id
}
return `${id}:${o.median_price_1000.currency}`
}
}).restore(initialState || {}),
...
})
)
This solved the caching problem without using fetchPolicy: 'no-cache'. There is probably an even better way to do this, but this works and is much better than disabling the cache entirely.
@markplindsay The problem is that Apollo should warn you in development mode when your dataIdFromObject() returns wrong values that cause problems with serialization.
@hwillson can confirm this happens with "@apollo/react-hooks@^3.1.3"
useQuery returns old data when variables change (with loading: true)
Edit: but maybe that is intentional and could make sense in some cases
@hwillson
Currently useQuery returns variables alongside data - as a user I'd expect these variables to be those used to fetch the old data (while new data is loading). However, they seem to be the same variables as those in the options argument, which doesn't seem very useful.
Same problem here, the data is always "one version" too old.
Same here.
Has anyone found a suitable workaround for this?
@afreix the trick of adding a key to the component was one approach that worked for me. Put one of the variables as the key and it should trigger the querying component to re-render.
@declanelcocks could you elaborate?
I'm having the same issue as others. I have a context in my React app that, when it changes, the id variable I have in my Query is changed and a new query should be called followed by updating the data.
When my context changes to a new variable, a query if executed and new data is populated. If I change my context back to a previously fetched variable, the id variable in the query gets updated but the query is not executed.
If I change my fetchPolicy to no-cache the query works as expected and refetches data every time my context is updated.
Yeah also experiencing this issue in 3.1.0 and 3.1.1.
I am facing the same issue with useLazyQuery. My Scenario is I am using fetchMore to load more data and the fetchMoreResult , variables variables inside updateQuery of fetchMore are different from that off onCompleted.
The order of Execution is as follows:
FetchMore -> UpdateQuery({fetchMoreResult , variables}) -> then OnCompleted is Called.
variables and fetchMoreResult in UpdateQuery are up to date; but the data parameter in Completed is not updated.
FYI it doesnt matter if my fetch policy is fetchPolicy:'network-only',
This still occurs on v2.6.8 for me which is a stable build. I think I have spent an honest 4 hours trying to fix this bug where
<Query
variables={{this changes}}
fetchPolicy='network-only'/>
// Check network status, it's always 7
// Data is fulfilled twice, once with older param second with newer param,
// causing intermittent ui flashes of data
</Query>
This still occurs on v2.6.8 for me which is a stable build. I think I have spent an honest 4 hours trying to fix this bug where
<Query variables={{this changes}} fetchPolicy='network-only'/> // Check network status, it's always 7 // Data is fulfilled twice, once with older param second with newer param, // causing intermittent ui flashes of data </Query>
@mhuconcern I am facing the exact same issue with the version you mentioned
This still occurs on v2.6.8 for me which is a stable build. I think I have spent an honest 4 hours trying to fix this bug where
<Query variables={{this changes}} fetchPolicy='network-only'/> // Check network status, it's always 7 // Data is fulfilled twice, once with older param second with newer param, // causing intermittent ui flashes of data </Query>@mhuconcern I am facing the exact same issue with the version you mentioned
@trdaya https://github.com/apollographql/react-apollo/issues/321#issuecomment-457224799 . If this helps you. I realize that this bug even though it exists on this build, it is exposed more if your graphql apis don't have a way to define if the returned object is unique to the request param
This still occurs on v2.6.8 for me which is a stable build. I think I have spent an honest 4 hours trying to fix this bug where
<Query variables={{this changes}} fetchPolicy='network-only'/> // Check network status, it's always 7 // Data is fulfilled twice, once with older param second with newer param, // causing intermittent ui flashes of data </Query>@mhuconcern I am facing the exact same issue with the version you mentioned
@trdaya #321 (comment) . If this helps you. I realize that this bug even though it exists on this build, it is exposed more if your graphql apis don't have a way to define if the returned object is unique to the request param
@mhuconcern thanks 👍 . I tried this but unfortunately, it didn't work for me.
Some assumptions and findings:
Since the fetchPolicy is cache-and-network:
networkStatus is 7 if the data (stale data) is already present in the cachenetworkStatus 7 (this time because data fetching from network is completed) with new data.I tried throttling my network on chrome's dev-tools and found that this issue doesn't happen on slow networks. networkStatus 7 is received only once, with the new data.
Just incase anyone else is having issues working around this, comparing current data to previousResult in updateQuery works for me. E.g.:
return fetchMoreResult.length === 0 || data !== previousResult
? previousResult
: // add new data
This is a pretty common issue. You need to do a check for equality of a name/id and show a loading component or pass an empty/default obj if you don’t want to see stale data. I would advise modifying the data variable, if that’s the same one from the query result obj. Just use a ternary or if/else. You may need to abstract it to an external component to ensure you get rerenders reliably.
…
On Wed, Jul 25, 2018 at 10:45 AM James Wyatt Cready-Pyle < @.*> wrote: Yeah, you basically have to use something like this to prevent rendering data from a query with different variables: if (data.reddit && data.reddit.subreddit.name !== this.state.search) { data = {}; } And that only works if the data being returned also includes the variable you passed in. Definitely seems like a bug. — You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub <#2202 (comment)>, or mute the thread https://github.com/notifications/unsubscribe-auth/AIM6lxQm220hAw84gsU2XQj043KYvC17ks5uKKCzgaJpZM4VZucg .
Worked for me. Thanks
In my case I am doing the following
let { data, loading, fetchMore, networkStatus } = useQuery<GetCompanyLeadsQuery, GetCompanyLeadsQueryVariables>(GET_COMPANY_LEADS_QUERY, {
variables: {
type: ContactType.Vendor,
query: queryField.value,
page: 1,
},
notifyOnNetworkStatusChange: true,
});
if(networkStatus === NetworkStatus.setVariables) {
data = undefined;
}
So when the query variable changes, the network status is set to setVariables and I set data to undefined to avoid rendering stale result for another query.
@krvajal As far as I can see, the problem there is that if you do have the data in the cache you end up wiping it out then with that approach.
The only approach I've currently seen working is somehow echoing some of the data included in your query variables in the data so you can see if the data is new or not.
This is a huge issue for applications that want to show up-to-date data, but still respond extremely quickly and opportunistically, and I've not seen a good solution that doesn't rely on using the response data (which isn't always possible).
This is proving to be such a big pain. Trying to fit in rxjs into the mix is fun(not). After spending a lot of time working with Angular, it's amusing to me that useLazyQuery doesn't even return a promise at this point. Maybe it's just me.
I am considering leaving development job and starting to work as a bus driver because of this bug.
@drudv I doubt that this will achieve desired outcome. It was already described above
that queryResult.variables are the same variables that were passed in the options argument.
Unless something has changed here, in that case, this would indeed simplify this issue.
I was able to solve this by using a combination of client.resetStore() on my logout function and fetchPolicy='network-only' on my Query.
I use @client directive to set a local value, when I have mutated it, Apollo will update it in next Microtask, so I read the previous result.
`js
new QueryData({
options: updatedOptions,
context: context,
onNewData: function () {
if (!queryData.ssrInitiated()) {
// update next tick
Promise.resolve().then(forceUpdate);
}
else {
forceUpdate();
}
},
});
Most helpful comment
I am considering leaving development job and starting to work as a bus driver because of this bug.