I am trying to start and stop a recursive fetch request with a button in React. I would also like to have a delay between each request. For this, I decided to use the useEffect hook and link it to a boolean state that is controlled by the button. Whenever this state updates, the script inside useEffect checks if it is true or false. If it's true, I would like to fetch some necessary data and initiate a recursive fetch function. If it's false, it means the recursive function is already running thus the ongoing request should be cancelled, stopping the recursion, and the setTimeout used to achieve the delay between requests should be cleared.
The code below is a mockup of how I attempted to solve this problem. However, it doesn't work, probably for the following reasons:
- The variables
requestandtimeoutare recreated on every rerender. I think this could be solved with theuseRefhook; however, I'm a beginner with React and haven't managed to get it to work. - My functions seem to be not getting any state updates; for example, in
recursiveFetch,data2is alwaysdefaultwhen it should contain a new value set by the fetch request inuseEffect.
I've tried many and many different combinations and ways to get this to work but didn't find any success. How should I approach this problem?
import { useState, useEffect } from 'react';
const Component = () => {
const [executing, setExecuting] = useState(false);
const [data1, setData1] = useState('default');
const [data2, setData2] = useState('default');
const [data3, setData3] = useState('default');
// I think these variables should be defined with `useRef` but I'm not sure...
let request;
let timeout;
const recursiveFetch = () => {
request = fetch(`https://website.com/${data2}`, { method: 'POST' })
.then((res) => {
timeout = setTimeout(() => {
recursiveFetch();
}, 1000);
}
}
useEffect(() => {
if(executing){
// Fetch some necessary data
fetch(`https://example.com/${data1}`)
.then((res) => res.json())
.then((data) => {
setData2(data.exampleProperty);
}
// Call recursive function
recursiveFetch();
} else {
// Cancel ongoing fetch request and clear timeout
request.cancel();
clearTimeout(timeout);
}
}, [executing])
return (
<>
<Button onClick={() => setExecuting((prev) => !prev)}></Button>
</>
);
};
export default Component;