How to call an API every 5 minutes using React? - reactjs

I've been trying to call the API every 5 min but the limit for setInterval doesn't allow that.
useEffect(() => {
setInterval(() => {
(() => {
const API_KEY = "C5EQJXXXXXXXXXXXX";
const name = "FB";
axios
.get(
`https://www.alphavantage.co/query?function=TIME_SERIES_INTRADAY&symbol=${name}&interval=5min&apikey=${API_KEY}`
)
.then(({ data }) => {
console.log(data);
console.log(data["Time Series (5min)"]);
for (let key in data["Time Series (5min)"]) {
setStocksX((prev) => [...prev, key]);
setStocksY((prev) => [
...prev,
data["Time Series (5min)"][key]["1. open"]
]);
}
//console.log(stocksX, stocksY);
});
})();
}, 30000);
});
Any advice would be appreciated!
Thanks

you can add setInterval with 60 * 5 * 1000 of time, in useEffect hook in first renderization. After you need clear setInterval when unmount component.
const ref = useRef(null)
useEffect(() => {
ref.current = setInterval(yourFunction, 5 * 60 * 1000);
return () => {
if(ref.current){
clearInterval(ref.current)
}
}
}, [])

I wrote this utility hook that I use in my React apps all the time:
import React from 'react';
type IntervalCallback = () => void;
function useDispatch(callback: IntervalCallback, delay: number): void {
const cachedCallback = React.useRef<IntervalCallback>();
React.useEffect(() => {
cachedCallback.current = callback;
}, [callback]);
React.useEffect(() => {
if (delay !== 0) {
const id = setInterval(() => cachedCallback?.current?.(), delay);
return () => clearInterval(id);
}
}, [delay]);
}
export const IntervalHooks = {
useDispatch,
};
I can then use it as such:
IntervalHooks.useDispatch(() => { console.log("hello"); }, 300000);
This gives me a lot of flexibility since I can dynamically update my interval if needed, and takes care of clean up when my component gets unmounted.

I presume that your hook is declaring a new setInterval every 0.5 seconds, possibly making your React App stop in order to avoid memory leak, you just need to declare one setInterval, this means that the second parameter of useEffect should have empty squared brackets
useEffect(() => {
setInterval(yourFunction, 300000); // The function will be called
},[]); // every 5 minutes

Related

Using useState and UseEffect in this way makes the slider for for the first times but then dosent work

const HeroSection = () => {
const [Change,setChange] = useState(false);
useEffect(() => {
setInterval(() => {
setChange(!Change)
}, 5000);
return () => clearInterval();
}, [Change]);
return (
<HeroContainer>
<HeroBg>
<ImageBg src={ Change ? Image1 : Image2 } />
</HeroBg>
</HeroContainer>
)``
}
export default HeroSection
react auto slide images works for the first few times then becomes buggy as in fast paced changes of images regardless of the 5 sec interval
clearInterval(); does nothing. You need to pass the created interval's ID to clearInterval for it to be cleared.
I also don't think the effect hook should have change as a dependency unless you use setTimeout instead.
const [change, setChange] = useState(false);
useEffect(() => {
const timeoutId = setTimeout(() => {
setChange(!change)
}, 5000);
return () => clearTimeout(timeoutId);
}, [change]);
to set a new timeout in the effect callback every time change changes.
Or:
const [change, setChange] = useState(false);
useEffect(() => {
const intervalId = setInterval(() => {
setChange(change => !change)
}, 5000);
return () => clearInterval(intervalId);
}, []);
to set an interval only once, when the component mounts.

React Lifecycles managing intervals with changing values

In one of my components I have a useEffect setup where an interval is set to fetch a function.
It's basically set up as follows:
...
const token = useToken() // custom hook that updates whenever access_token updates
const fetchData = useCallback(() => {
callAPI(token)
}, [token])
useEffect(() => {
if (!token) return
fetchData()
const interval = setInterval(fetchData, 60000)
return () => {
clearInterval(interval)
}
}, [token]}
...
It is supposed fetchData every 60 seconds which it does.
What it also needs to do (which it doesn't) is whenever the token is updated, it should account for that.
What I've currently done to try solve that is clear the interval when the token changes and start the process over. But I think I've handled that incorrectly in my attempts above.
Any idea on how to accomplish this correctly?
the only thing missing is fetchData should be added to the dependency array to make sure that the useEffect uses the updated callback
useEffect(() => {
if (!token) return
fetchData()
const interval = setInterval(fetchData, 60000)
return () => {
clearInterval(interval)
}
}, [token, fetchData])
but you can also move the fetchData(this time fetchData doesn't have to be memorized with useCallback) function inside the useEffect, that way you can only have token as a dependency:
useEffect(() => {
const fetchData = () => {
if(!token) return;
callAPI(token)
};
fetchData();
const interval = setInterval(fetchData, 60000)
return () => {
clearInterval(interval)
}
}, [token])
Edit:
You can remove token form the useEffect this way:
const fetchData = useCallback(() => {
if(!token) return; // moved the token check here
callAPI(token)
}, [token])
useEffect(() => {
fetchData();
const interval = setInterval(fetchData, 60000)
return () => {
clearInterval(interval)
}
}, [fetchData])

Call API Every X Seconds in React Function Component

I have the following react class component to call an API every 10 seconds. Its works with no issues.
class Alerts extends Component {
constructor() {
this.state = {
alerts: {},
}
}
componentDidMount() {
this.getAlerts()
this.timerId = setInterval(() => this.getAlerts(), 10000)
}
componentWillUnmount() {
clearInterval(this.timerId)
}
getAlerts() {
fetch(this.getEndpoint('api/alerts/all"))
.then(result => result.json())
.then(result => this.setState({ alerts: result }))
}
render() {
return (
<>
<ListAlerts alerts={this.state.alerts} />
</>
)
}
}
I am trying covert this to a react functional component. This is my attempt so far.
const Alerts = () => {
const [alerts, setAlerts] = useState([])
useEffect(() => {
getAlerts()
setInterval(() => getAlerts(), 10000)
}, [])
getAlerts() {
fetch(this.getEndpoint('api/alerts/all"))
.then(result => result.json())
.then(result => setAlerts(result)
}
return (
<>
<ListAlerts alerts={alerts} />
</>
)
}
Please can someone help me complete the example? Is useEffect the correct usage or is there a better option?
Any help would be appreciated
One issue here is that this.getEndpoint will not work from a function component. It seems the original Alerts class component is missing some code since that must be implemented somewhere.
Another issue is that the interval is not being cleaned up - you should return a cleanup function from the effect body to clear the timer.
Lastly there's no reason to re-define getAlerts on every render, defining it once inside of the effect body would be better.
After cleaning up some missing parens, etc. my final implementation would look something like:
function getEndpoint(path) {
return ...; // finish implementing this
}
const Alerts = () => {
const [alerts, setAlerts] = useState([])
useEffect(() => {
function getAlerts() {
fetch(getEndpoint('api/alerts/all'))
.then(result => result.json())
.then(result => setAlerts(result))
}
getAlerts()
const interval = setInterval(() => getAlerts(), 10000)
return () => {
clearInterval(interval);
}
}, [])
return (
<>
<ListAlerts alerts={alerts} />
</>
)
}
I found this blog by Dan Abramov which explains the idea of a useInterval hook that solves this problem.
You can use it like this :
function Counter() {
useInterval(() => {
callMyApi()
}, 1000);
}
And declare the useInterval hook this way :
import React, { useState, useEffect, useRef } from 'react';
function useInterval(callback, delay) {
const savedCallback = useRef();
// Remember the latest callback.
useEffect(() => {
savedCallback.current = callback;
}, [callback]);
// Set up the interval.
useEffect(() => {
function tick() {
savedCallback.current();
}
if (delay !== null) {
let id = setInterval(tick, delay);
return () => clearInterval(id);
}
}, [delay]);
}
Hope it helps someone!

React hook to wait until the previous call is complete

I am working on a react project which uses hooks. And I was assigned a task
"change the useInterval hook, or create a new one (usePoll?). This should operate the same as useInterval, but should wait until the ajax request is complete before starting the timer".
I am new to react hooks and was looking for a solution for this but could not find. Current useInterval function is as follows.
import React, { useEffect, useRef } from 'react';
export function useInterval(callback, delay, immediate = true) {
const savedCallback = useRef();
// Remember the latest callback.
useEffect(() => {
savedCallback.current = callback;
}, [callback]);
// Set up the interval.
useEffect(() => {
function tick() {
savedCallback.current();
}
if (delay !== null) {
if (immediate) {
tick();
}
let id = setInterval(tick, delay);
return () => clearInterval(id);
}
}, [delay]);
}
and it use in the program as follows.
useInterval(() => {
get(`/api/v1/streams/1`).then(({ data: { data } }) => {
setStream(data);
});
}, 5000);
and I need to change the useInterval function to wait until the ajax request is complete before starting the timer. It would be great if anyone can help me on this. Thanks
Give this a shot.. it requires calling the next function inside of then but it should come close to what you're looking for.
function useInterval(handler, delay, immediate = true) {
React.useEffect(() => {
let interval
const start = () => {
clearInterval(interval)
interval = setInterval(() => handler(start), delay)
}
handler(start)
return () => clearInterval(interval)
}, [])
}
usage:
useInterval((next) => {
get('/api/v1/streams/1').then(data => {
// tell the timer to begin
next()
})
}, 5000)
You can use async\await to await for first call completes.
Modify internal useEffect like so
export function useInterval(callback, delay, immediate = true) {
const savedCallback = useRef();
// Remember the latest callback.
useEffect(() => {
savedCallback.current = callback;
}, [callback]);
// Set up the interval.
useEffect(() => {
// useEffect doesn't like async callbacks (https://github.com/facebook/react/issues/14326) so create nested async callback
(async () => {
// Make tick() async
async function tick() {
await savedCallback.current();
}
if (delay !== null) {
if (immediate) {
await tick(); // Here we should await for tick()
}
let id = setInterval(tick, delay); // Unfortunately setInterval is not async/await compatible. So it will not await for tick
return () => clearInterval(id);
}
})(); // Call nested async function
}, [delay]);
}
And you callback should return Promise so async\await to work properly
useInterval(() => {
// After .then promise will be resolved, so our useInterval will know about that
return get(`/api/v1/streams/1`).then(({ data: { data } }) => {
setStream(data);
});
}, 5000);

Request to server each N seconds after flag

I have a specific case. The first thing I do is request the Index.DB. After I got the taskId from it, I need to start asking the server every 5 seconds. And stop doing this on a specific flag. How can i do that properly with hooks?
I'tried to use useInterval hook like this:
https://github.com/donavon/use-interval;
But when i set it in useEffect causes consistent error:
Invalid hook call. Hooks can only be called inside of the body of a function component.
const Page = () => {
const [task, setTask] = useState({})
const isLoaded = (task.status === 'fatal');
const getTask = (uuid: string) => {
fetch(`${TASK_REQUEST_URL}${uuid}`)
.then(res => {
return res.json();
})
.then(json => {
setTask(json.status)
})
.catch(error => console.error(error));
};
useEffect(() => {
Storage.get('taskId')
.then(taskId => {
if (!taskId) {
Router.push('/');
}
useInterval(() => getTask(taskId), 5000, isTaskStatusEqualsSomthing)
})
}, []);
return (
<p>view</p>
);
};
I also tried to play around native setInterval like this
useEffect(() => {
Storage.get('taskId')
.then(taskId => {
if (!taskId) {
Router.push('/');
}
setInterval(() => getTask(taskId), 5000)
})
}, []);
But in this case i don't know how to clearInterval and also code looks dirty.
The solution is simple. You just need to configure your setInterval within .then callback like
useEffect(() => {
let timer;
Storage.get('taskId')
.then(taskId => {
if (!taskId) {
Router.push('/');
else {
timer = setInterval(() => getTask(taskId), 5000)
}
}
})
return () => {clearInterval(timer)}
}, []);
The reason, first approach doesn't work for you is because you cannot call a hook conditionally or in useEffect as you did for useInterval

Resources