DEV Community

Cover image for Interval fetching with react-query
Allan López
Allan López

Posted on • Edited on

Interval fetching with react-query

Photo by Emily Morter on Unsplash

Recently I was asked to implement a page that needs to work with a fetching interval, as I wanted to keep the server state as simple as posible I decided to give react-query a try and implement a interval logic on it. Here is what I came up with it.

Use case

We need to make a post to an endpoint to start a process. Then every 5 seconds ask for its progress, once we received that the process is finished we must stop fetching.

As a plus I wanted to keep this on a hook in order to be used in different parts of the app. Lets start.🤓

Code

First we need to create a hook with a mutation inside in order to start the process. We add the stop state in order to tell us when to stop the fetching and we save the process id we get from the response.

const useProcessInterval = ({ onSuccess, onError }) => {
  const [processId, setProcessId] = useState(null);
  const [stop, setStop] = useState(false);

  // Mutation to start the process
  const { mutate } = useMutation(startProcess, {
    onMutate: () => {
      //When mutate we want to start fetching
      setStop(false);
    },
    onError: error => {
      console.error(error);
      //Stop fetching
      setStop(true);
      onError();
    },
     onSuccess: data => {
      setProcessId(data.process_id);
    },
  });
};
Enter fullscreen mode Exit fullscreen mode

For the interval fetching query part, we are going to take advantage of the refetchInterval method we have with react-query.

//Fetch until received status is finished
  const { isLoading, data } = useQuery(['processProgress', processId], getProgress, {
    onSuccess: data => {
      if (data.status === 'finished') {
        // Stop fetching
        setStop(true);
        //Clean up processId
        setProcessId(null);
        onSuccess();
      }
    },
    onError: error => {
      console.error(error);
      setStop(true);
      setProcessId(null);
      onError();
    },
    //Only start getting process when we have received the process id from the mutation
    enabled: processId != null,
    //Keep refetching every 5 seconds while we don't stop it
    refetchInterval: stop ? false : 5000,
    refetchIntervalInBackground: true,
    refetchOnWindowFocus: false,
  });
Enter fullscreen mode Exit fullscreen mode

Finally we end up with the final hook.

const useProcessInterval = ({ onSuccess, onError }) => {
  const [processId, setProcessId] = useState(null);
  const [stop, setStop] = useState(false);

  // Mutation to start the process
  const { mutate } = useMutation(startProcess, {
    onMutate: () => {
      setStop(false);
    },
    onError: error => {
      console.error(error);
      setStop(true);
      onError();
    },
     onSuccess: data => {
      setProcessId(data.process_id);
    },
  });

  //Fetch until received status is finished
  const { isLoading, data } = useQuery(['processProgress', processId], getProgress, {
    onSuccess: data => {
      if (data.status === 'finished') {
        setStop(true);
        setProcessId(null);
        onSuccess();
      }
    },
    onError: error => {
      console.error(error);
      setStop(true);
      setProcessId(null);
      onError();
    },
    enabled: processId != null,
    refetchInterval: stop ? false : 5000,
    refetchIntervalInBackground: true,
    refetchOnWindowFocus: false,
  });

  return { mutate, data, isLoading };

};
Enter fullscreen mode Exit fullscreen mode

And we can use it in our component the following way.

 const { mutate, data, isLoading } = useProcessInterval({
   onSuccess: () => console.log('Process finished'),
   onError: () => console.log('Error with process'),
 });
Enter fullscreen mode Exit fullscreen mode

See you in the next posts! 🖐

Top comments (6)

Collapse
 
tkdodo profile image
Dominik D

Good use-case, thanks for the article. I'd still like to point out that this is not long-polling in an http sense, where the server keeps the connection open until the information is available. What you are doing here is a "normal", traditional polling where the client initiates a new request at an interval level.

see: en.wikipedia.org/wiki/Push_technol...

Maybe you can point that out somewhere, because that is somewhat confusing :)

Collapse
 
allanloji profile image
Allan López

Thanks for the comment, totally agree in changing that to avoid confusion 👍

Collapse
 
seblean profile image
seb • Edited

Anyone know what startProcess and getProgress are for?

Also useQuery does not take onSuccess or onError. So I'm not sure where they've come from.

Docs: tanstack.com/query/latest/docs/fra...

Collapse
 
seblean profile image
seb • Edited

This article no longer works in the latest version of React Query:
tkdodo.eu/blog/breaking-react-quer...

Collapse
 
vietphong profile image
vietphong

thanks

Collapse
 
stanislas profile image
Stanislas

Thank you for this!