The idea is to update the timerState by adding +1s It will trigger useEffect hook and set a timer for 1000 ms Timer will change timerState with +1s, which will trigger useEffect again... On component unmount timer is destroyed Btw we skip useEffect on first render with firstRenderRef flag On timer pause we destroy timer Note that timer is kept in useRef global variable, because we need to access it from different places On reset button we reset all variables & fool useEffect telling it is the first render, which he will skip import React, { useEffect, useRef, useState } from 'react'; import secToHHMMSS from '../../../helpers/functions/secToHHMMSS'; function Component() { const [timerState, setTimerState] = useState(0) const intervalRef = useRef(null) const firstRenderRef = useRef(true) useEffect(() => { if (firstRenderRef.current) { firstRenderRef.current = false return } intervalRef.current = setInterval(function() { setTimerState(prevVal => prevVal + 1) }, 1000) return () => clearInterval(intervalRef.current) }, [timerState]) function resetTimer() { setTimerState(0) clearInterval(intervalRef.current) firstRenderRef.current = true } return ( <> <div>Timer <b>{secToHHMMSS(timerState)}</b></div> <button onClick={() => setTimerState(timerState + 1)}>Start</button> <button onClick={() => clearInterval(intervalRef.current)}>Pause</button> <button onClick={resetTimer}>Reset</button> </> ) }