3

I am using "react-query" to call an API from a component . For the purpose of this question , I am returning a mock response from the API .

Every time , I open the dropdown , the useQuery function is called which in turn calls the mock API .

App.js

import React from 'react';
import './style.css';
import { QueryClient, QueryClientProvider } from 'react-query';
import { DropDown } from './Dropdown.js';
const queryClient = new QueryClient();
export default function App() {
  return (
    <QueryClientProvider client={queryClient}>
      <div style={{ display: 'flex', justifyContent: 'center' }}>
        <DropDown />
      </div>
    </QueryClientProvider>
  );
}

Dropdown.js

import React from 'react';
import { useQuery } from 'react-query';

export const DropDown = () => {
  console.log('DropDown re-rendered');

  const { data, isLoading, isError } = useQuery('API', () => {
    return new Promise((resolve, reject) => {
      console.log('API called');
      resolve(['mockData']);
    });
  });

  return (
    <>
      <select>
        <option> 1 </option>
        <option> 2 </option>
      </select>
    </>
  );
};

You can find the demo here : https://react-quxtxd.stackblitz.io

In the console you will see that every time you open the dropdown , useQuery is called.

Stackblitz Editor url : https://stackblitz.com/edit/react-quxtxd?file=src/Dropdown.js

As an alternative to avoid this , I can use the traditional useEffect to make the API calls but I was looking at leveraging the caching advantage that useQuery provides but I am stuck due to this "re-rendering" issue .

Any suggestions / modifications ?

Ananth Kumble
  • 152
  • 2
  • 15
  • What is the purpose of the API call? You may want to consider moving the call to a parent component and passing in the data from the call as props to the dropdown component – gloo May 05 '22 at 16:36
  • The purpose of the API call is to populate the dropdown . I have tried moving the api call to a parent component but even that results in re-rendering the parent . Basically in whichever component I move the API call to - that component re-renders every time the child component dropdown is opened – Ananth Kumble May 05 '22 at 16:38
  • Have you seen this post https://stackoverflow.com/questions/67040687/react-query-doesnt-seem-to-be-caching – gloo May 05 '22 at 16:48
  • No I haven't but in the documentation it is mentioned about "refetchOnWindowFocus: false" . This is true by default. When I set this to false , the API is not called. – Ananth Kumble May 05 '22 at 17:02
  • You may also want to look at `staleTime` and `cacheTime`: (https://stackoverflow.com/questions/71282427/how-to-fetch-user-details-only-once) – gloo May 05 '22 at 17:13
  • I opened the link in the question and it calls the API just once. So either the problem has been fixed or I'm not sure what the problem is in the first place. – Jakub Kotrs May 05 '22 at 21:43
  • Yeah I addressed it using refetchOnWindowFocus . It also works using staleTime : Infinity . Thanks @gloo – Ananth Kumble May 05 '22 at 21:51

2 Answers2

6

This works for me

{ refetchOnWindowFocus: false }

Usage:

const { data, status } = useQuery("users", fetchUsers, { 
    refetchOnWindowFocus: false 
});

If all your queries need this config. It's good to add this to defaultOptions of your queryClient

// App.tsx

const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      refetchOnWindowFocus: false,
    },
  },
});

<QueryClientProvider client={queryClient}>
...
</QueryClientProvider>
Anjan Talatam
  • 2,212
  • 1
  • 12
  • 26
2

It seems that the original stackblitz has been fixed, so the issue is no longer reproducible. For posterity:

You've probably seen a background refetch due to focusing the window. This is because staleTime defaults to 0 and refetchOnWindowFocus defaults to true. You can either turn off the flag, or set a higher staleTime (recommended).

TkDodo
  • 20,449
  • 3
  • 50
  • 65