Cristian Flórez
Cristian Flórez

Reputation: 2781

React Query: rerun onSuccess method of a cached query

What I have

I am getting the user's location (latitude/longitude) which I use to call a google geocode API, unless the user's coords change, the request is not running again, since the query it uses the user's coords as queryKey array dependecy.

The problem

the problem is that I'm running some operations in the onSuccess query method, this method is only run when any of the queryKey dependencies change, and I mentioned this not happen.

How to run the onSuccess method whether the queryKey dependencies change or not?

Reference code

export const useGoogleReverseGeocoding = (coords) => {
  const url = 'someUrl';
  const request = createClient(); // axios abstraction

  return useQuery({
    queryKey: ['google-geocode', coords],
    queryFn: request,
    enabled: !!coords,
    onSuccess: (data) => {
      const searchTerm = removeGlobalCodeText(data?.plus_code?.compound_code);
      // set searchterm in a global store. This searchterm change with 
      // different user actions, so if the user re-share his location
      // I need to run onSuccess transformation again.
      setSearchTerm(searchTerm); 
    },
  });
};

Upvotes: 0

Views: 1985

Answers (1)

ivanatias
ivanatias

Reputation: 4033

As I was explaining in my comment, onSuccess can't be fired without the query itself firing again. Since certain user actions should trigger the transformations on onSuccess, you have a couple of ways to go about this, one of them would be to move these transformations on a useEffect hook and add some user action related flag on the dependencies array. The other proposed solution would be to invalidate the query upon these user actions, so it will be refetched and the transformations on onSuccess will execute.

You can achieve this using useQueryClient hook which returns the current QueryClient instance. You can invalidate the query from anywhere as long as the component is wrapped by QueryClientProvider. For this example and for convenience, I will include this hook on useGoogleReverseGeocoding custom hook.

Example:

Custom hook:

export const useGoogleReverseGeocoding = (coords) => {
  const queryClient = useQueryClient()
  const url = 'someUrl';
  const request = createClient(); // axios abstraction

  const geocodingData = useQuery({
    queryKey: ['google-geocode', coords],
    queryFn: request,
    enabled: !!coords,
    onSuccess: (data) => {
      const searchTerm = removeGlobalCodeText(data?.plus_code?.compound_code);
      // set searchterm in a global store. This searchterm change with 
      // different user actions, so if the user re-share his location
      // I need to run onSuccess transformation again.
      setSearchTerm(searchTerm); 
    },
  });
  
  const invalidateQueryOnAction = () => queryClient.invalidateQueries(['google-geocode'])
  
  return { geocodingData, invalidateQueryOnAction }
};

Some component:

const dummyCoords = {
    lat: 33.748997,
    lng: -84.387985
}

const SomeComponent = () => {
  const { geocodingData, invalidateQueryOnAction } =
    useGoogleReverseGeocoding(dummyCoords)

  const handleSomeUserAction = () => {
    // handle action...

    // Invalidate query, so the query gets refetched and onSuccess callback executes again
    invalidateQueryOnAction()
  }
}

PS: If @TkDodo comes along with a different solution for this, I would suggest to go for it instead.

Upvotes: 1

Related Questions