React Hooks setInterval with dependency trigger multiple interval - reactjs

I have a useEffect with setInterval who refresh each seconds data depending on an user id
useEffect(() => {
const interval = setInterval(() => {
refetchUserId(userId);
}, 1000);
return () => clearInterval(interval)
}, [userId]);
But when my userId change (with a setState) the interval is not cleared and i have a lot of getUserId fired
How to fix this
Thanks

Related

How to call an API every 5 minutes using React?

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

Stop setInterval function written inside a useEffect when navigated to another page

I have an API that needs to be fetched every 30 seconds.The UseEffect written below in the code is for a component that gets rendered on home component. It's working well but if I navigate to any another page I need the API to not be fetched.
I'm using react-router and redux.
useEffect(() => {
dispatch(loadCurrencyList())
setInterval(() => {
dispatch(loadCurrencyList())
}, 30000
)
}, [dispatch])
Do a cleanup in the return of useEffect :
useEffect(()=>{
const timer = setInterval(...)
return ()=> clearInterval(timer)
}, [dispatch])
Store timer id in a mutable variable created with useRef() hook,
then use it in the component unmount code. Don't return clean up function from useEffect() dependent on [dispatch] as it will be executed every time dispatch variable changes.
const timer = useRef();
useEffect(() => {
dispatch(loadCurrencyList());
timer.current = setInterval(() => {
dispatch(loadCurrencyList())
}, 30000);
}, [dispatch]);
useEffect( () => () => {
if (timer.current) {
clearInterval(timer.current);
}
}, []);

React useEffect prevent on initial mount

i have five a snapshot listener in useEffect and i have another call api to get data from firestore and update state
but I am facing a problem is every initial mount all listener got called , my goal is i want to all listener called only when document changed
i tried with useRef it works but listener do not trigger
As you can see in the example below, onSnapshot is printed during the initial mounted
useEffect(() => {
if (isFirstMount.current) return;
someFirestoreAPICall.onSnapshot((snap) => {
//called every initial mount
});
someFirestoreAPICall.onSnapshot((snap) => {
//called every initial mount
});
}, []);
useEffect(() => {
if (isFirstMount.current) {
isFirstMount.current = false;
return;
}
}, []);
useEffect(() => {
const fetchData = async () => {
setLoading(true);
let snap = await someFirestoreAPICall.get();
setData(snap.docs.map((doc) => ({ ...doc.data(), id: doc.id })));
setLoading(false);
};
fetchData();
}, []);
Codesandbox
You can use a condition inside your useEffect block as you are doing, I think. But perhaps useState would be more appropriate here than useRef.
eg:
const [state, setState] = useState(null)
useEffect(()=>{
if (state) {
// do something
}
}, [state])
The useEffect will run on mount and every time you change the value of state, but code inside the condition will only run if you change the state to a truthy value.

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])

How to slowdown/debounce events handling with react hooks?

Handle scroll event will fire to often. What is the way to slowdown/debounce it?
And if it's possible, i want last event always be fired and not skipped.
const handleScroll = event => {
//how to debounse scroll change?
//if you will just setValue here, it's will lag as hell on scroll
}
useEffect(() => {
window.addEventListener('scroll', handleScroll)
return () => {
window.removeEventListener('scroll', handleScroll)
}
}, [])
Here is the useDebounce hook example from xnimorz
import { useState, useEffect } from 'react'
export const useDebounce = (value, delay) => {
const [debouncedValue, setDebouncedValue] = useState(value)
useEffect(
() => {
const handler = setTimeout(() => {
setDebouncedValue(value)
}, delay)
return () => {
clearTimeout(handler)
}
},
[value, delay]
)
return debouncedValue
}
Event handler that uses hooks can be debounced done the same way as any other function with any debounce implementation, e.g. Lodash:
const updateValue = _.debounce(val => {
setState(val);
}, 100);
const handleScroll = event => {
// process event object if needed
updateValue(...);
}
Notice that due to how React synthetic events work, event object needs to be processed synchronously if it's used in event handler.
last event always be fired and not skipped
It's expected that only the last call is taken into account with debounced function, unless the implementation allows to change this, e.g. leading and trailing options in Lodash debounce.
const debounceLoadData = useCallback(debounce((debounceValue)=>{
props.setSenderAddress(debounceValue)
}, 300), []);

Resources