
Security News
Deno 2.2 Improves Dependency Management and Expands Node.js Compatibility
Deno 2.2 enhances Node.js compatibility, improves dependency management, adds OpenTelemetry support, and expands linting and task automation for developers.
use-dispose-uncommitted
Advanced tools
For the vast majority of cases, use useEffect, and the cleanup function for side effects and cleanups, and KEEP YOUR RENDER FUNCTION PURE
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
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.
Install the package yarn add use-dispose-uncommitted
// default import also works
import { useDisposeUncommitted } from "use-dispose-uncommitted";
function MyComponent() {
useDisposeUncommitted(function disposer() {
console.log('Component disposed by React');
}, function reviver(revivedFromRenderBeforeCommit) {
if (revivedFromRenderBeforeCommit) {
console.log('Component speculatively disposed by us, but React suddenly re-rendered it');
} else {
console.log('Component speculatively disposed by us, but React suddenly mounted it');
}
});
}
Naive Mobx's useObserver impl:
import { Reaction } from "mobx";
function useObserver(jsxFactoryFunction) {
const [__, forceUpdateCounter] = useState(0);
function forceUpdate() {
forceUpdateCounter(c => c +1);
}
const reactionRef = useRef<Reaction>(null);
const mountingRef = useRef<{ changedBefore: boolean, isMounted: boolean }>({
changedBefore: false,
isMounted: false
});
function createReaction() {
reactionRef.current = new Reaction(() => {
if (mountingRef.isMounted) {
forceUpdate();
} else {
mountingRef.changedBefore = true;
}
});
}
useDisposeUncommitted(() => {
reactionRef.current.dispose();
reactionRef.current = null;
}, (revivedFromRenderBeforeCommit) => {
createReaction();
});
useLayoutEffect(() => {
if (mountingRef.changedBefore) {
forceUpdate();
}
mountingRef.isMounted = true;
return function unMountCleanup() {
reactionRef.current.dispose();
reactionRef.current = null;
}
}, []);
if (reactionRef.current === null) {
createReaction();
}
}
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.
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.
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.
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
Some code paths are not covered by tests (revive before mount), Need to more investigation how to trigger that code path.
FAQs
README.md
The npm package use-dispose-uncommitted receives a total of 342 weekly downloads. As such, use-dispose-uncommitted popularity was classified as not popular.
We found that use-dispose-uncommitted demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 2 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
Deno 2.2 enhances Node.js compatibility, improves dependency management, adds OpenTelemetry support, and expands linting and task automation for developers.
Security News
React's CRA deprecation announcement sparked community criticism over framework recommendations, leading to quick updates acknowledging build tools like Vite as valid alternatives.
Security News
Ransomware payment rates hit an all-time low in 2024 as law enforcement crackdowns, stronger defenses, and shifting policies make attacks riskier and less profitable.