Research
Security News
Kill Switch Hidden in npm Packages Typosquatting Chalk and Chokidar
Socket researchers found several malicious npm packages typosquatting Chalk and Chokidar, targeting Node.js developers with kill switches and data theft.
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 1,470 weekly downloads. As such, use-dispose-uncommitted popularity was classified as 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.
Research
Security News
Socket researchers found several malicious npm packages typosquatting Chalk and Chokidar, targeting Node.js developers with kill switches and data theft.
Security News
pnpm 10 blocks lifecycle scripts by default to improve security, addressing supply chain attack risks but sparking debate over compatibility and workflow changes.
Product
Socket now supports uv.lock files to ensure consistent, secure dependency resolution for Python projects and enhance supply chain security.