Reputation: 1379
I have a simple code which iterates through an array and logs them in the interval of 1000 ms as such :
const arr = [1, 2, 3];
let i = 0;
const choice = () => {
const interval = setInterval(() => {
console.log(arr[i++ % arr.length]);
if (i === 8) {
clearInterval(interval);
}
}, 1000);
};
choice();
Upon introducing React state into the code, the whole thing goes absolutely mad and starts counting out of the interval to a point that I reach almost infinite loop although the simple console.log
instead of react state works fine.
const [ele, setEle] = React.useState(null);
const arr = [1, 2, 3];
let i = 0;
const choice = () => {
const interval = setInterval(() => {
setEle(arr[i++ % arr.length]);
if (i === 8) {
clearInterval(interval);
}
}, 1000);
};
choice();
return(
<h1>{ele}</h1>
)
I wonder how I can achieve the effect using the state with the current code.
https://codesandbox.io/s/condescending-dubinsky-kbl5m
Upvotes: 1
Views: 73
Reputation: 2916
I found the other answer somewhat unsatisfying because it requires a new timeout to be generated each time. It is possible to set up a single setInterval which requires a useRef to get an up-to-date setI into the timer. The empty useEffect dependencies ensures the setInterval is not re-run on each state update.
const App = () => {
const [i, setI] = React.useState(0);
const timer = React.useRef();
timer.current = () => setI(i+1);
React.useEffect(() => {
const interval = setInterval(() => {
timer.current();
}, 1000);
return () => {
clearInterval(interval)
};
}, [])
return <div>{i}</div>;
}
Upvotes: 0
Reputation: 370589
With your current implementation, every render of the component is initializing a new interval, so you get lots of intervals running simultaneously, each of which initiate even more intervals. Use setTimeout
instead, so that every render only initializes exactly one action to take place in the future.
Since it's the index of the array that changes each time, consider using that as state instead:
const App = () => {
const [i, setI] = React.useState(0);
const arr = [1, 2, 3];
if (i !== 8) {
setTimeout(() => {
setI(i + 1);
}, 1000);
}
return (
<div className="App">
<h1>{arr[i % arr.length]}</h1>
</div>
);
}
ReactDOM.render(
<App />,
document.body
);
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
Upvotes: 4