UNPKG

use-initializer

Version:

A React hook for once-only initialization with dependency tracking and cleanup

61 lines (53 loc) 1.72 kB
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<T, Deps = unknown[]>( initialize: (dependencies: Deps[]) => T, dispose?: (v: T) => void, deps: Deps[] = [] ): T { const depsRef = useRef<Deps[]>([]); const dependenciesChanged = !areDependenciesEqual(depsRef.current, deps); depsRef.current = deps; const ref = useRef<T | null>(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: unknown[], nextDeps: unknown[]): boolean { // 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 }