Reputation: 75
I am trying to create a simple counter component in React. It should start at 0, then iterate up to the passed in number. Here is the code I currently have:
const Counter = ({ number }) => {
const [currentNumber, setCurrentNumber] = useState(0);
for (let i = 0; i < number; i++) {
setTimeout(() => setCurrentNumber(currentNumber + 1), 2000);
}
return <span>{currentNumber}</span>;
};
export default Counter;
What happens when I run this, is it basically keeps counting forever. currentNumber
never stops incremementing, even once the number
has been reached.
Upvotes: 0
Views: 151
Reputation: 9178
You should add currentNumber
as a dependency for useEffect()
. This way useEffect()
will get triggered every second and a new timeout will be registered but only as long as currentNumber
is smaller than number
.
const Counter = ({ number }) => {
const [currentNumber, setCurrentNumber] = React.useState(0);
React.useEffect(() => {
if(currentNumber < number) setTimeout(() => setCurrentNumber(currentNumber + 1), 1000);
}, [currentNumber]);
return <span>{currentNumber}</span>;
};
ReactDOM.render(<Counter number={20}/>, document.getElementById('root'));
<script crossorigin src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>
<div id="root"></div>
Upvotes: 2
Reputation: 184
Please try this code:
const [currentNumber, setCurrentNumber] = useState(0);
useEffect(() => {
if (currentNumber >= number) return;
setTimeout(() => {
setCurrentNumber(currentNumber + 1);
}, 1000);
}, [currentNumber]);
return <span>{currentNumber}</span>;
Upvotes: 0
Reputation: 44
I ran the code and it's getting stuck. Reason:
You're scheduling 5 timers which will all update currentNumber
to currentNumber + 1
. currentNumber
is 0
, so it will update number
times to 1
after 2 seconds. You can approach this with an interval or a useEffect
:
useEffect(() => {
if (currentNumber < number)
setTimeout(() => setCurrentNumber(n => n + 1), 2000)
}, [number, currentNumber])
If you're using an interval, make sure you return a cleanup callback from useEffect
, like that:
useEffect(() => {
const id = useInterval(...)
return () => clearInterval(id)
}, [...])
Upvotes: 0