226

I am new to this react-query library.

I know that when I want to fetch data, with this library I can do something like this:

const fetchData = async()=>{...}

// it starts fetching data from backend with this line of code
const {status, data, error} = useQuery(myKey, fetchData());

It works. But how to trigger the data fetching only when a button is clicked? , I know I probably could do something like <Button onPress={() => {useQuery(myKey, fetchData())}}/> , but how to manage the returned data and status...

16 Answers 16

249

According to the API Reference, you need to change the enabled option to false to disable a query from automatically running. Then you refetch manually.

// emulates a fetch (useQuery expects a Promise)
const emulateFetch = _ => {
  return new Promise(resolve => {
    resolve([{ data: "ok" }]);
  });
};

const handleClick = () => {
  // manually refetch
  refetch();
};

const { data, refetch } = useQuery("my_key", emulateFetch, {
  refetchOnWindowFocus: false,
  enabled: false // disable this query from automatically running
});

return (
  <div>
    <button onClick={handleClick}>Click me</button>
    {JSON.stringify(data)}
  </div>
);

Working sandbox here  

Bonus: you can pass anything that returns a boolean to enabled. That way you could create Dependant/Serial queries.

// Get the user
const { data: user } = useQuery(['user', email], getUserByEmail)
 
// Then get the user's projects
const { isIdle, data: projects } = useQuery(
  ['projects', user.id],
  getProjectsByUser,
  {
    // `user` would be `null` at first (falsy),
    // so the query will not execute until the user exists
    enabled: user,
  }
)
Sign up to request clarification or add additional context in comments.

14 Comments

Why not using useMutation instead of useQuery?
If it helps someone, refetch() returns a promise and can resolve it to get the data of the response if you want to do something synchronously.
We cannot use mutations for a get call, we can use them for put/post/delete calls react-query.tanstack.com/guides/mutations
@ADB I can't find anything in the documentation that says "useMutation cannot be used with get request", can you point me to the right place?
This works but is not recommended by the primary maintainer. Instead use queryClient.fetchQuery()
|
33

You have to pass the manual: true parameter option so the query doesn't fetch on mount. Also, you should pass fetchData without the parentheses, so you pass the function reference and not the value. To call the query you use refetch().

const {status, data, error, refetch} = useQuery(myKey, fetchData, {
      manual: true,
    });

const onClick = () => { refetch() }

Refer to the manual querying section on the react-query docs for more info https://github.com/tannerlinsley/react-query#manual-querying

1 Comment

I think "manual" (that still exists) is replaced by "enabled"(in V2). github.com/tannerlinsley/…
23

There are two problems with refetch():

  • it will send a request to the server even if the response is already cached and the staleTime has not expired
  • you can't pass params to it

If you need to make a query on an event like onClick or onChange, use queryClient.fetchQuery().

function MyComponent() {
  const queryClient = useQueryClient();

  const handleOnClick = async () => {
    const data = await queryClient.fetchQuery({ queryKey, queryFn });
  }

  return <button onClick={handleOnClick}>My Button</button>;
}

You can see the creator and current maintainer recommending this approach.

3 Comments

Okay but then what about isLoading and isError and all the other conveniences of useQuery?
@V.Rubinetti you can add the state yourself with two booleans. fetchQuery() throws, so you'd ideally have it in a try/catch, if you catch anything, set isError to true, and before/after the try/catch you'd set your loading state
@V.Rubinetti you can also consider alternatives, like having your button click set some state which impacts the enabled condition of useQuery()
20

Looks like the documentation changed and is missing the manual querying section right now. Looking at the useQuery API however, you'd probably need to set enabled to false, and then use refetch to manually query when the button is pressed. You also might want to use force: true to have it query regardless of data freshness.

Comments

8

At first react query gives us enabled option and by default it is true

const fetchData = async()=>{...}

const {status, data, error , refetch} = useQuery(myKey, fetchData() , {
enabled : false
}
);

<button onClick={() => refetch()}>Refetch</button>

2 Comments

r u sure that enabled is true by default?
Yah if enabled is not true then how can react query fetch data in initial render look up to react query docs they purely assign that enabled is true it is pretty common right cause we always want the data in initial render so enabled is always true.
6

You can try this version:

const fetchData = async()=>{...}

// it starts fetching data from backend with this line of code
const {status, data, error, refetch } = useQuery(
myKey, 
fetchData(),
{
  enabled: false,
}
);
const onClick = () => { refetch() }
// then use onClick where you need it

From documentation Doc:

enabled: boolean

  • Set this to false to disable this query from automatically running.
  • Can be used for Dependent Queries.

refetch: (options: { throwOnError: boolean, cancelRefetch: boolean }) => Promise<UseQueryResult>

  • A function to manually refetch the query.
  • If the query errors, the error will only be logged. If you want an error to be thrown, pass the throwOnError: true option
  • If cancelRefetch is true, then the current request will be cancelled before a new request is made

Comments

4

There is another way to do this that also works if you want to trigger multiple refetches.

const [fetch, setFetch] = useState(null);
const query = useQuery(["endpoint", fetch], fetchData);

const refetch = () => setFetch(Date.now());

// call the refetch when handling click.

If you want to refetch multiple entities you could have a top level useState that is called for instance fetchAll and:

...
const query = useQuery(["endpoint", fetch, fetchAll], fetchData);
...

and this code will also trigger if you press a button to fetch all.

2 Comments

sadly Not work! It's not refetch if the fetch changes
@OliverD I'm using this, so most likely you have some other issue
2

If the key is the same, then use refetch(), if the key is different then use useState to trigger the query.

For example:

const [productId, setProductId] = useState<string>('')
const {status, data, error, refetch} = useQuery(productId, fetchData, {
      enable: !!productId,
    });

const onClick = (id) => { 
if(productId === id) {
  refetch() 
}
else {
 setProductId(id)
}


}

1 Comment

not enable but enabled
1

As mentioned by others in this thread, using query.refetch does make a request even if the cache is not stale. So ideally, we would not call query.refetch only if query.isFetched && !query.isStale. I wanted to encapsulate that logic inside a hook and this is how I solved it:

// useGetFoo.ts
export const useGetFoo = () => {
  const query = useQuery({
    queryKey: ["foo"],
    queryFn: fetcher,
    enabled: false,
  });

  const fetch = (): ReturnType<typeof query.refetch> => {
    if (query.isFetching) return Promise.resolve(query);
    if (query.isFetched && !query.isStale && !query.isError)
      // browser hangs without this setTimeout
      return new Promise((resolve) => setTimeout(() => resolve(query)));
    return query.refetch();
  };

  return { ...query, fetch };
};

And then we can use it like this:

// SomeComponent.tsx
const { data, fetch: getFoo } = useGetFoo();

useEffect(() => {
  void getFoo();
}, [getFoo]);

Comments

1
const { data, isLoading, refetch } = useQuery({
  queryKey: ["users"],
  queryFn: fetchUsers,
  enabled: false,
});


return (
  <div>
    <Link to={"/"}>home</Link>
    <h1>Users</h1>

    <button onClick={refetch}>fetch data</button>
    <hr />

    {isLoading && <h1>loading...</h1>}
    {data?.data.map((user) => (
      <p key={user.id}>{user.name}</p>
    ))}
  </div>
);

In React Query v5, fetching occurs when a page mounts. To disable fetching on mount, set enabled to false in the options for useQuery. You can then use the refetch function to manually fetch data when needed by your event.

Comments

1

The correct and suggested approach is using fetchQuery from the QueryClient to call the query asynchronously. For the loading, state, you can use a regular state variable and toggle it accordingly.

export default function User() {
 const [loading, setLoading] = useState(false);
 const [users, setUsers] = useState([])
 const queryClient = useQueryClient();

 async function fetchData() {
    try {
      setLoading(true);
      const response = await queryClient.fetchQuery({
        queryKey: ["get-users"],
        queryFn: () => getUsers(),
      });

      // Perform any functionality using the query response. 
     // For example, you can set a state with the data response.
    if (response.success && response.statusCode === 200) {
      setUser(response.data)
     }    

    } catch (error) {
      console.error("Unknown error occurred", error)
    } finally {
      setLoading(false);
    }
  }

  return (
    <div>
      <button onClick={fetchData}>Get Users</button>
      {loading && <div>Loading Users...</div>}
      <div>{JSON.stringify(users, null, 2)}</div>
    </div>
  )
}

Comments

0

In react-query there is an option for useQuery() to trigger the hook whenever you want. The option is enabled which only takes boolean. Here's an example:

const useGetAllUsers = useQuery({
    queryKey:["users"],
    queryFn: your_fetch_request,
    enabled: true|false
})

you can pass any boolean state from that component here.

Comments

0

refetch: (options: { throwOnError: boolean, cancelRefetch: boolean }) => A function to manually refetch the query. If the query errors, the error will only be logged. If you want an error to be thrown, pass the throwOnError: true option cancelRefetch?: boolean Defaults to true Per default, a currently running request will be cancelled before a new request is made When set to false, no refetch will be made if there is already a request running.

const {isPending, data, refetch} = useQuery({
queryKey: ['yourKey'],
queryFn: ({signal}) => apiService.get("/myapiendpoint", {signal}),
enabled: false,
})

<button
   onClick={() =>
     refetch({ throwOnError: true, cancelRefetch: false })} >
</button>

Comments

0

I solved my problem by invalidating the cache of the query and fetchQuery again, here is part of my code:

export const useDeviceMutation = ({
    ID,
}: {
  ID: string
}) => {
  return useMutation({
    mutationFn: deviceMutation,
    onSuccess: () => {
      queryClient.invalidateQueries(['devices', ID])
      queryClient.fetchQuery(['devices', ID], () =>
        getDetails(ID)
      )
    },
  })
}

Comments

-2

User query on click is it will only fetch the data when user click or user allow Earlier any website will fetch data by default whenever you use axios for get data now userquery on click help to fetch data only when you clicked on a button so for this you have to do 2 things

  1. Use Usequery for fetching data by disabling the button for default fetching
  2. Usequery gives you one thing refetch which will help you below is the code
const fetchSuperHeroes=()=>{
return axios.get("localhost:4000/superheroes")
const {isLoadind,data,error,isError,refetch}=useQuery('super-heroes',fetchSuperHeroes{
enable:false
}

Comments

-12

you can use useLazyQuery()

import React from 'react';
import { useLazyQuery } from '@apollo/client';

function DelayedQuery() {
   const [getDog, { loading, error, data }] = useLazyQuery(GET_DOG_PHOTO);

   if (loading) return <p>Loading ...</p>;
   if (error) return `Error! ${error}`;

   return (
      <div>
         {data?.dog && <img src={data.dog.displayImage} />}
         <button onClick={() => getDog({ variables: { breed: 'bulldog' } })}>Click me!</button>
      </div>
   );
}

reference: https://www.apollographql.com/docs/react/data/queries/#manual-execution-with-uselazyquery

1 Comment

This is wrong answer because it uses apollo client which is a different library from react-query and doesnt apply in this context. Plus that is graphql the question is for REST API.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.