i'm trying to do countdown timer with react. It will be basically countdown from 10 to 0 and when 0 i will call some function.
i found ideally for me some example: https://codesandbox.io/s/0q453m77nw?from-embed but it's a class component i wan't to do that with functional component and hooks but i can't.
i tried:
function App() {
const [seconds, setSeconds] = useState(10);
useEffect(() => {
setSeconds(setInterval(seconds, 1000));
}, []);
useEffect(() => {
tick();
});
function tick() {
if (seconds > 0) {
setSeconds(seconds - 1)
} else {
clearInterval(seconds);
}
}
return (
<div className="App">
<div
{seconds}
</div>
</div>
);
}
export default App;
it's count down from 10 to 0 very quickly not in 10 seconds. where i mistake ?
It appears the multiple useEffect
hooks are causing the countdown to run more than once per second.
Here's a simplified solution, where we check the seconds
in the useEffect
hook and either:
setTimeout
to update seconds
after 1 second, orThere are some downsides to this method, see below.
function App() {
const [seconds, setSeconds] = React.useState(10);
React.useEffect(() => {
if (seconds > 0) {
setTimeout(() => setSeconds(seconds - 1), 1000);
} else {
setSeconds('BOOOOM!');
}
});
return (
<div className="App">
<div>
{seconds}
</div>
</div>
);
}
ReactDOM.render(<App />, document.getElementById('root'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.12.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.11.0/umd/react-dom.production.min.js"></script>
<div id="root"></div>
Downsides
Using setInterval
has the downside that it could be stopped - for example, the component is unmounted, you navigate to a different tab, or close your computer. If the timer requires more robustness, the better alternative would be to store an endTime
in the state (like a global store or context) and have your component check the current time against the endTime
to calculate the countdown.