setInterval does not take the state of the hook - javascript

I call an api every x time with setInterval I must pass some parameters to the api to get the data, my parameters are state hooks, the problem is that when I update a state hook for example "ChoiceIpGroup" the setinterval always takes the initial value and not the update of the state of the hook. What could be happening ?
useEffect(() => {
let interval = setInterval(() => {
//getKPIMetricas(setdata, dataFilter)
getMetricsInGroups(dataFilter, setDataKPisGroups, choiceIpGroup)
}, 4000);
return () => clearInterval(interval);
}, []);
I update the hook states from the front, when I change the hook state it works, but when the setinterval is updated the hook state is updated to the initial parameter. what is an empty array
const [choiceIpGroup, setChoiceIpGroup] = useState([])
and when updating the hook it would look like this:
const [choiceIpGroup, setChoiceIpGroup] = useState([ "0", "1" ])
but when setinterval() acts it doesn't take the update of the hook's state, only the initial state.

setInterval is a method that calls a function or runs some code after specific intervals of time, as specified through the second parameter. For example, the code below schedules an interval to print the phrase: “Interval triggered” every second to the console until it is cleared. setInterval(() => { console.

Related

Not updating the State inside the function but updating in useEffect

I'm a beginner to React JS.
I faced this weird situation,
const [counter, setCounter] = useState(0);
const incrementCounter = () => {
setCounter(counter + 1);
console.log(counter, "Log inside the function");
};
useEffect(() => {
console.log(counter, "Log inside the useEffect");
}, [counter]);
return (
<div>
<h1>{counter}</h1>
<button onClick={incrementCounter}>Increment</button>
</div>
);
So when incrementCounter function is triggered counter value will be increased by 1 and right after that I'm logging the value in the console. But it displays the value as 0. When I use the useEffect hook to check the changes of count state it I'm getting the correct value as 1 when I log the count value inside the useEffect scope. What is the reason for not displaying in incrementCounter function and the reason for displaying the correct value inside the useEffect hook.
according to React docs
Think of setState() as a request rather than an immediate command to update the component. For better perceived performance, React may delay it, and then update several components in a single pass. In the rare case that you need to force the DOM update to be applied synchronously, you may wrap it in flushSync, but this may hurt performance.
setState() does not always immediately update the component. It may batch or defer the update until later. This makes reading this.state right after calling setState() a potential pitfall. Instead, use componentDidUpdate or a setState callback (setState(updater, callback)), either of which are guaranteed to fire after the update has been applied. If you need to set the state based on the previous state, read about the updater argument below.
States value will change only after the component has rendered.
In this case the log inside the function will keep the state value even after the SetCounter, once it completes the component will render again.
The useEffect is triggered when the state has changed on such render so it shows the new value.
A new incrementCounter function object is being redefined with every render. Each time it is defined, its own internal counter is based on the value of counter state at that point. The counter within that particular incrementCounter function object is stale and doesn't get updated anymore.
This is because the setCounter is asynchronous.
If you use setTimeOut you can clearly observe it
setTimeout(() => {
console.log(counter, "Log inside the function");
}, 2000);

Why do you need to use clearInterval to reset the timer in React?

In the following code:
useEffect(() => {
const interval = setInterval(() => {
setSeconds(seconds => seconds + 1);
}, 1000);
return () => clearInterval(interval);
}, []);
Why do you need to return a cleanup function that resets the interval object? In my understanding, since the interval function object was not created with useCallback(), the function should be re-initialized with each render and therefore have no memory of past interval values. Why is a cleanup function needed to clear it before the next render?
Why is a cleanup function needed to clear it before the next render?
(emphasis mine)
It's not and it's not what this code does.
By passing an empty dependency list to useEffect ([]) you are effectively telling React to only run this hook the first time the component renders, not(!) every time the component re-renders.
Now, clearing the interval when the component gets unmounted/destroyed (which is what this code does) is necessary, otherwise the interval would keep running (and throw an error eventually since you cannot update the state of an unmounted component).

React setInterval function keeps running in the background after rerender

I am using setInterval inside useEffect with an empty dependency array inside one of my react components.
There is true/false useState which controls the display of that component.
When the state is false, the component is hidden and when it's true the component is shown.
Something like this:
const [state, setState] = useState(false)
// in jsx render section
return (
<div>
{state? <component/> : '' }
</div>
)
When the component loads for the first time the setInterval runs only one time which is what it supposes to do.
If the state goes to false then back to true, the component is removed from the UI and is then displayed back again. However, what happens here is now I have two setInterval functions running in the background, and the first one doesn't shutdown.
The number of the setInterval functions keeps increasing with each time that component re-render.
I don't understand how React works in this situation.
I need to know how it works (i.e. why won't the function shutdown, why are the number of functions increasing) and how to fix it.
This is the structure of React useEffect.React performs the cleanup when the component unmounts.
useEffect(() => {
//effect
return () => {
//cleanup runs on unmount
}
}, [])
The cleanup function should have clearInterval() which will basically removes setInterval or stops it when component unmounts. See the practical code below:
let intervalid;
useEffect(
() => {
intervalid = setInterval(() => {console.log("Iterate");}, 1000));
return function cleanup() {
console.log("cleaning up");
clearInterval(intervalid);
};
},
[]
);
This above code is just for understanding approach. Every effect may return a function that cleans up after it. This lets us keep the logic for adding and removing subscriptions close to each other. # FROM REACT DOCS Reference

Change text every 3 seconds React useEffect

I'm trying to change my text every 3 seconds using useEffect() and setInterval(). Right now it only changes the text ONE time then it doesn't change it anymore.
What am i doing wrong?
EDIT: I'm using the library "react-spring"
const [toggle, setToggle] = useState(false)
useEffect(() => {
setInterval(() => {
switch (toggle) {
case false: setToggle(true)
break;
case true: setToggle(false)
break;
}
}, 3000);
}, [])
{transitions.map(({ item, props }) => item
? <animated.div style={props}>Text that changes #1</animated.div>
: <animated.div style={props}>Text that changes #2</animated.div>)}
Solution:
useEffect(() => {
const intervalID = setTimeout(() => {
setToggle((toggle) => !toggle)
}, 3000);
return () => clearInterval(intervalID);
}, []);
Important points:
The dependency array([]) should be empty. This way you ensure that you're gonna execute this effect only on the initial mounting of the component. You need only a single interval and its creation and destruction are not dependent on a single variable but the component itself. If you put toggle in the dependency array, you will run this effect every time the variable changes thus effectively making YET ANOTHER interval on every 3 seconds. If you did supply a clean-up function, this would still work but it would be more like a setTimeout. However, in your case(without a clean-up function), this will simply introduce infinite number of intervals which will compete with each other.
You have to supply an updater function to setToggle instead of a simple value. This ensures that you're using the most current state for the update and not the stale one in the closure. If you simply provide a value, the interval is making a closure over your initial value and thus updating it. In this way, you will always update the initial false to true and this will repeat forever, leaving you with a "constant" true value.
As you're using an interval, you should provide a clean-up function to the useEffect to clear the interval on component dismount. This is very important as skipping this part will introduce memory leaks and also bugs as you'll try to update a component even after its dismount from the DOM.
Try this way
useEffect(() => {
setTimeout(() => setToggle((prevToggle) => !prevToggle), 3000);
}, [toggle]);

React hooks useEffect running even with [] given as deps

I am facing quite a weird issue. Below is the code
const Counter = () => {
const [count, setCount] = useState(0);
useEffect(() => {
const id = setInterval(() => setCount(count => count + 1), 1000);
return () => {
clearInterval(id);
};
}, []);
return <div>{count}</div>;
};
As per my knowledge, since I have given an empty array, the useEffect will only run after the first render. Also, since I am clearing the interval COUNT MUST NOT BE UPDATED AFTER COUNT=1. Still the setInterval seems to be running continuously. Can anyone please explain it?
Is it so that since I am given [] as deps the interval is somehow not being cleared?
clearInterval(id) - this will get invoked just before component gets unmounted. As long as the component is not unmounted the function returned from useEffect will not invoked. So the interval in not cleared and the state will continue updating thus the count increases. The interval gets cleared when the component Counter gets unmounted
This is why React also cleans up effects from the previous render before running the effects next time.
Yes, React will clean up before running the effects next time, not right after running the current effects.
Here's a brief explanation.
In your case, clearInterval(id) will only be executed when the Counter component is unmounted.
edited:
I have created a sandbox example to show when the effect is executed. Based on this example I updated my original "graph" because it looks react run the "clean effect" after rerendering.
The effect is only setting up the interval callback to update your state. setInterval will continue to run on the interval, use setTimeout instead to only run the state update once. Still return a clearTimeout in case the component unmounts within the timeout period.
Edit: More Detailed Explanation
By using an empty dependency array you are telling react that running your effect isn't dependent on any external values. The effect will run once when the component mounts (setting up the setInterval) and never run again since it isn't dependent on anything. When the component is unmounting react will run all the returned effect "cleanup" functions, i.e. the clearInterval call.
What this leaves you with is this:
Component mounts
Effect runs: sets up interval, returns cleanup function
interval is running
update count after 1000ms
update count after 1000ms
update count after 1000ms
update count after 1000ms
Component unmounts: react runs effect cleanup function, clears interval
Suggested Fix: Use setTimeout to only update state once
const Counter = () => {
const [count, setCount] = useState(0);
useEffect(() => {
const id = setTimeout(() => setCount(count => count + 1), 1000);
return () => {
clearTimeout(id);
};
}, []);
return <div>{count}</div>;
};

Categories