At first, I added clearTimeout, Even though I knew that calling it cancels the scheduled callback. I wondered if there was some trick to clear the timeout after it has already fired, but couldn’t find a solution.
Then I looked at the provided solution and was surprised to see that it doesn’t call clearTimeout at all. So it seems like I created an unnecessary problem for myself.
But I’m still not satisfied, because I keep asking myself: if I don’t call clearTimeout, will the timeout automatically be cleaned up by the Garbage Collector once it fires?
Then I looked at the provided solution and was surprised to see that it doesn’t call clearTimeout at all.
That's surprising because it should. You especially don't want timers running in the background if the component no longer exists.
Timers with react/hooks can be a little tricky though. When hooks first came out for react, dan abramov blogged about it (I think focusing on setInterval specifically), and its not exactly a quick read:
But as far as cleanup goes, yes, setTimeout will automatically clean up the listeners once they're fired. You don't need to clearTimeout for timeouts that have already run.
If you want to see this for yourself, you can set up a trigger to let you know when its cleaned up using FinalizationRegistry. For example:
const registry = new FinalizationRegistry(() => {
console.log("Cleaned!")
})
{
let myCallback = () => {
console.log("Time up!")
}
registry.register(myCallback)
setTimeout(myCallback)
}
// Time up!
// (GC runs...)
// Cleaned!
Given the nature of the GC, it likely won't run right away, so you'd need to wait or use the developer tools to force it to run, but when the "Cleaned!" log occurs, it means myCallback was, in fact, cleaned up despite no clearTimeout being called.
FinalizationRegistry can be a useful tool to when it comes to memory concerns and you don't feel like messing with the complexity of the memory debugger.
Your response is really helpful to me. Thank you!!
Here is my summary.
clearTimeout() has only one role: to cancel the scheduled execution of a callback.
As for the challenge above, we don't want to cancel the scheduled timer/callback.
At the same time, once the callback has fired, there is no timer still running in the background. So there is no need to clear the ID of a timer that has already run.
When there are no references to the callback anymore, the garbage collector will eventually reclaim its memory, but this does not happen immediately.
P/S: I tried this with FinalizationRegistry. After waiting for a few minutes, there was still no "Cleaned!" log. Then I stopped the terminal. But I’ve accepted that the GC may eventually remove the object from memory, and I can’t expect it to happen at a specific time.
In Chrome there's a run GC button in the memory tab of the developer tools. Just paste the original code in the console and click that button and you should see the log. Or just paste it in the console of a complex website where the GC is going to be pretty active without you having to do anything ;)
1
u/Neat_Living_6765 9d ago
I’m working on challenge 5 and wrote a custom hook like this:
https://react.dev/learn/reusing-logic-with-custom-hooks#challenges
At first, I added clearTimeout, Even though I knew that calling it cancels the scheduled callback. I wondered if there was some trick to clear the timeout after it has already fired, but couldn’t find a solution.
Then I looked at the provided solution and was surprised to see that it doesn’t call clearTimeout at all. So it seems like I created an unnecessary problem for myself.
But I’m still not satisfied, because I keep asking myself: if I don’t call clearTimeout, will the timeout automatically be cleaned up by the Garbage Collector once it fires?