When variables change, data is the previous result in Query Component
See original GitHub issueContinuing discussion from the bug apollographql/react-apollo#2202
Intended outcome:
I want the data
returned from useQuery
to be undefined when the variables change. Reasoning is that if the variables for the query change, loading
is set to true, but data
remains set to the data from the previous query with old variables.
Actual outcome:
data
is set to the previous variables data
while the next query (with new variables) is in flight.
How to reproduce the issue: https://codesandbox.io/s/changing-variables-demo-obykj
Discussion
While this may be intended and desired for most cases, I think it would be a good change to add the option for developers to choose if they want this behavior. One example (that caused me to find this issue) was a search bar that searches as you type, but doesn’t search for anything (and hides results) if you’ve only typed less than 3 characters. My issue was that the user could search for something, delete everything, then re-search with different text. When the third character was pressed, skip
would be set to false, and the search would happen, but there would be a brief moment of showing the data from the old query variables.
I looked at apollographql/react-apollo#2889 by @jcready which had a potential fix for this issue, but looks abandoned. But following the discussion, a few possibilities were discussed as potential fixes to this issue:
- Adding a
shouldInvalidatePreviousData
option touseQuery
. This would allow a developer to write code such as:
const { data, loading, error } = useQuery(FIND_DOGS, {
variables: {
search: searchText
},
shouldInvalidatePreviousData(nextVariables, previousVariables) {
return nextVariables !== previousVariables;
}
});
- Adding a
clearPreviousDataOnLoad
option touseQuery
. This would allow a developer to write code such as:
function usePrevious(value) {
const ref = useRef();
useEffect(() => {
ref.current = value;
}, [value]);
return ref.current;
}
...
const previousSearchText = usePrevious(searchText);
const { data, loading, error } = useQuery(FIND_DOGS, {
variables: {
search: searchText
},
clearPreviousDataOnLoad: searchText !== previousSearchText
});
- A final option that I see as more of a workaround that requires no changes: https://codesandbox.io/s/cool-brown-ttwxw
function usePrevious(value) {
const ref = useRef();
useEffect(() => {
ref.current = value;
}, [value]);
return ref.current;
}
...
const previousSearchText = usePrevious(searchText);
const { data, loading, error } = useQuery(FIND_DOGS, {
variables: {
search: searchText
}
});
const showDataWhileLoading = searchText === previousSearchText
const updatedData = !loading || showDataWhileLoading ? data : undefined;
I feel the third option is weird to write, and while the first option is probably the easiest from a developer’s perspective, the second option would be the easiest to implement
Versions
System:
OS: macOS 10.15.3
Binaries:
Node: 10.18.1 - /usr/local/bin/node
Yarn: 1.21.1 - ~/.npm-global/bin/yarn
npm: 6.13.1 - ~/.npm-global/bin/npm
Browsers:
Chrome: 80.0.3987.132
Firefox: 69.0.2
Safari: 13.0.5
npmPackages:
@apollo/client: ^3.0.0-beta.39 => 3.0.0-beta.39
@apollo/react-components: 3.2.0-beta.0 => 3.2.0-beta.0
@apollo/react-hoc: 3.2.0-beta.0 => 3.2.0-beta.0
apollo-cache-persist: ^0.1.1 => 0.1.1
apollo-client: ^2.6.4 => 2.6.8
apollo-link-error: ^1.1.12 => 1.1.12
npmGlobalPackages:
apollo: 2.21.3```
Issue Analytics
- State:
- Created 4 years ago
- Reactions:8
- Comments:11 (3 by maintainers)
Top GitHub Comments
Hi all, I just wanted to add to this issue because although this change is reasonable, it reverses a major pattern from the past 2 years of Apollo client and there are probably a handful of apps like mine that didn’t realize this behavior was unintentional until we upgraded and found this issue.
In my app, we had deliberately used this behavior so the user could still view a table of data instead of a loading spinner. For example, consider:
The loading state of the
TableComponent
above was just to change the opacity, so all of the previous data was still visible while loading. The<LoadingSpinner />
was supposed to be a fallback for basically the first page load only.With #6566, now we see a loading spinner any time the query updates, no more seeing the opacity change on the existing data (as can be expected)
Luckily in our repo we have a handy custom hook called
usePreviousNonNullish
(inspired by this and this blog post) that keeps a ref to the prior version of a variable so I was able to re-implement this feature like so:Custom Hook
Usage
So I mainly wanted to leave this example here in case anyone upgrades and their stuff isn’t working anymore.
But I also wanted to follow up with @benjamn because originally @davismariotti suggested adding a possible query option to preserve this behavior. Should I go ahead and track my own previous data or would this be something apollo could provide? Or maybe I’d be able to leverage the cache for this?
Thank you 🙏, apologies for commenting on the closed issue
In general, I agree with @clayne11’s comment https://github.com/apollographql/react-apollo/pull/1639#issuecomment-465766827, and I would love to stop delivering previous data at all, without any special configuration around when it might be safe to deliver.