Warning ⚠️☣️🐉 you probably don't need it
For the vast majority of cases, use useEffect, and the cleanup function for side effects and cleanups, and KEEP YOUR RENDER FUNCTION PURE
Ah, still here? useDisposeUncommitted
useDisposeUncommitted is a tiny React hook to help us clean side effects of uncommitted components.
Based on similar implementation of MobX's internal hook.
React discussion in this topic: #15317 [Concurrent] Safely disposing uncommitted objects
When do we ever need that? - Explainer
It is know that, side effects in react should be only inside useEffect
, and React will run them only when the component instance is actually being committed/mounted.
React may (For various reasons: suspense, strick-mode, aborted-renders) decide to throw away component instance after render, but before running useEffects
, without letting us know in any mean.
As long as we keep side-effects in useEffects
it's not a problem, But lets take MobX as example:
In order to track access to observables, MobX must create the reaction on render phase.
And in case this React will throw away tje component instance, we will not have a chance to dispose the Mobx reaction, which means memory leaks and possible bugs.
Simple usage example
Install the package yarn add use-dispose-uncommitted
// default import also works; { ;}
Naive Mobx's useObserver impl:
;
Some implementation details
Using FinalizationRegistry
On js engines that supports
FinalizationRegistry (chromium 84 , firefox 79 , node 14), we allocate React state without creating any external reference, and we register it for cleanup on FinalizationRegistry.
As only React have reference to that state object, we can tell that React have disposed the component when the cleanup callback is called for that state object.
Using timer based GC
For platform that does not support FinalizationRegistry, We take a speculative assumption that is a specific period of time have passed since render, but the component wasn't committed, we assume it was disposed by read.
That period of time is a hard-coded 10 seconds, none configurable, as in mobx impl.
Component revival
That speculation can bring us to a situation where we've ran our disposer, but React suddenly committing/re-rendering the component.
for that situation, we also have the reviver function that will run and signal that.
Read this comment
Not sure if it's a good idea to use FinalizationRegistry for that?Testing, Running, Building.
This project is using yarn 2 pnp zero install.
Which means you just need to have yarn installed (version not matter) clone it, and run yarn test
or yarn build
.
Node 14 is required to run the tests suite
Tests coverage
Some code paths are not covered by tests (revive before mount), Need to more investigation how to trigger that code path.