Skip to content

When variables change, data is the previous result in Query Component #6039

@davismariotti

Description

@davismariotti

Continuing 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:

  1. Adding a shouldInvalidatePreviousData option to useQuery.
    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;
  }
});
  1. Adding a clearPreviousDataOnLoad option to useQuery.
    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
});
  1. 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```

Metadata

Metadata

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions