use-initializer
Version:
A React hook for once-only initialization with dependency tracking and cleanup
48 lines (47 loc) • 1.55 kB
JavaScript
import { useEffect, useRef } from 'react';
/**
* useMemo & useState are not suitable for once-only initialization
* reinitialized on dependency change
* https://github.com/facebook/react/issues/27735#issuecomment-1885624506
*/
export function useInitializer(initialize, dispose, deps = []) {
const depsRef = useRef([]);
const dependenciesChanged = !areDependenciesEqual(depsRef.current, deps);
depsRef.current = deps;
const ref = useRef(null);
if (ref.current === null || dependenciesChanged) {
// dispose any previous instance
if (ref.current !== null) {
dispose?.(ref.current);
}
ref.current = initialize(deps);
}
// dispose on unmount
useEffect(() => {
return () => {
if (ref.current !== null) {
dispose?.(ref.current);
ref.current = null;
}
};
}, []);
return ref.current;
}
/**
* Compares two dependency arrays using Object.is, just like React does internally
*/
function areDependenciesEqual(prevDeps, nextDeps) {
// First, check if both are arrays and have the same length
if (!Array.isArray(prevDeps) ||
!Array.isArray(nextDeps) ||
prevDeps.length !== nextDeps.length) {
return false;
}
// Check each dependency using Object.is
for (let i = 0; i < prevDeps.length; i++) {
if (!Object.is(prevDeps[i], nextDeps[i])) {
return false; // Found a difference
}
}
return true; // All dependencies are the same
}