Reputation: 237
I'm using a useEffect()
hook in React to fetch data on an interval every five seconds. When my app first loads, the initial fetch request takes five seconds because it's in the setInterval()
function.
I'm trying to make the API call on page load and then every five seconds after that, make API call on the interval to retrieve new data.
What I've tried that's not working:
useEffect(() => {
await updateData(id, state, setState)
.then(() => {
const interval = setInterval(async () => {
if (id) {
await updateData(id, state, setState); // API call
}
}, 5000);
return () => {
clearInterval(interval);
};
},[lensName, state, setState])
}
What I'm currently doing and would like to improve:
useEffect(() => {
// Make API call, once initial call is made and response is returned make calls on a 5 second interval
const interval = setInterval(async () => {
if (id) {
await updateData(id, state, setState); // API call
}
}, 5000);
return () => {
clearInterval(interval);
};
}, [lensName, state, setState])
}
Any help is greatly appreciated.
Upvotes: 0
Views: 7118
Reputation: 76
the important thing to note here is that your updateData function should return a promise to make await work then your above logic will work perfectly. It will wait until the first API call is not finished before going to the second line.
useEffect(() => {
await updateData(id, state, setState);
const interval = setInterval(async () => {
if (id) {
await updateData(id, state, setState); // API call
}
}, 5000);
//update function would be like:
function updateData(id, state, setState) {
...
return API.get("/url");
}
}, []);
Upvotes: 1
Reputation: 353
The following solution works fine, modify it as per your needs:
function App() {
const [count, setCount] = useState(0);
const myDummyApi = async () => {
for (let i = 0; i < 10 ** 9; i++) {
const val = i;
}
return { data: "some data" };
};
useEffect(() => {
if (count === 0) { // condition for checking if the API call being made is initial one or not.
myDummyApi().then((data) => {
setCount(count + 1);
});
} else {
const timer = setTimeout(() => {
myDummyApi().then((data) => {
setCount(count + 1);
clearTimeout(timer);
});
}, 5000);
}
}, [count]);
return (
<div className="App">
<span>{`Api Call ${count}`}</span>
</div>
);
}
Full code can be found here in sandbox.
count > 0
the API call will be made only after timeout of 5 secs.Upvotes: 1
Reputation: 2523
You can add another useEffect
without dependency to call api when page is first load. However, it's better to show the logic of updateData
that we can know what you want to do.
// Call api when first load
useEffect(() => {
await updateData(id, state, setState);
}, [])
// After, every five seconds to call api
useEffect(() => {
const interval = setInterval(async () => {
if (id) {
await updateData(id, state, setState); // API call
}
}, 5000);
return () => {
clearInterval(interval);
};
}, [lensName, state, setState])
Upvotes: 1
Reputation: 3899
I would use two useEffect()
calls: one for the 5 second poll, and one that fires only once (with an empty dependency array). Something like this:
// Make API call once
useEffect(() => {
const live = true;
if (id) {
await updateData(id, state, setState, live);
}
return () => { live = false; }
}, []);
// Make API call on a 5 second interval
useEffect(() => {
const live = true;
const interval = setInterval(async () => {
if (id) await updateData(id, state, setState, live);
}, 5000);
return () => {
live = false;
clearInterval(interval);
}
}, [lensName, state, setState]);
Also note that you'll want some sort of flag to let your updateData()
function know whether the component is still mounted. If it gets unmounted, you don't just want to cancel the interval, you'll also want to avoid calling setState()
.
Upvotes: 1
Reputation: 828
You can use time value outside the useEffect hook. Increment it every 5 seconds and pass it as second argument of useEffect. Whenever this time value gets changed, UseEffect will get triggered and it will run the function inside it.
const [timeInterval, setTimeInterval] = useState(0);
setTimeout(() => {
setTimeInterval(timeInterval + 1);
}, 5000);
useEffect(() => {
await updateData(id, state, setState); // API call
}, [timeInterval]);
Upvotes: 1