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.
CodePudding user response:
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>
CodePudding user response:
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)
}, [...])
CodePudding user response:
Please try this code:
const [currentNumber, setCurrentNumber] = useState(0);
useEffect(() => {
if (currentNumber >= number) return;
setTimeout(() => {
setCurrentNumber(currentNumber 1);
}, 1000);
}, [currentNumber]);
return <span>{currentNumber}</span>;