I'd like to clarify my understanding of what's happening here. Any detail to improve my current understanding'd be appreciated.
function Timer() { let [time, setTime] = useState(5); useEffect(() => { let timer = setInterval(() => { setTime(time - 1); }, 1000) return () => clearInterval(timer); }, ); return <div>{time}</div> } export default Timer https://codesandbox.io/s/cranky-chaplygin-g1r0p
timeis being initialised to5.useEffectis read. Its callback must be made ready to fire later.- The
divis rendered. useEffect's callback is executed.setInterval's callback gets ready to fire. SurelyuseEffect'sreturnstatement doesn't fire here, because if it did it would cancel the timer (and the timer does work).- After, roughly, 1 second,
setInterval's callback fires changing the state oftime(to 4). - Now that a piece of state has changed, the function is re-executed.
time, a new variable, is initialised to the new time state. - A new
useEffectis read, it's callback made ready to fire later. (This happens because there is no 2nd argument ofuseEffect()). - The component function's
returnstatement is executed. This effectively re-renders thediv. - At some point, the previous
useEffect'sreturnstatement executes (which disables thetimerin that previoususeEffect). I'm not sure when this occurs. - The 'new'
useEffect's callback is executed.
useEffecthas a dependancy array, if you don't use it, useEffect will get called every state change.. For a timer like this passing[]to your dependency will stop the Timer from constantly been created / destroyed. PS: blindly passing[]to all useEffects is not always a good idea, make sure any dependency's that should effect your render are also included,SetTimeoutinstead ofSetIntervalwould be better in your example, or if you do useSetIntervaluse the dependency[], Also to make this a bit more full-proof, async stuff like timers etc should do a mounted check, otherwise you could end up callingsetStateon an unmounted compoenent.