mirror of
https://github.com/zoriya/v10.git
synced 2026-08-07 22:56:34 +00:00
46 lines
1.4 KiB
TypeScript
46 lines
1.4 KiB
TypeScript
'use client';
|
|
|
|
import { useEffect, useRef } from 'react';
|
|
|
|
import { useLatestRef } from './use-latest-ref';
|
|
|
|
interface Destroyable {
|
|
destroy(): void;
|
|
}
|
|
|
|
/**
|
|
* Destroy an instance on unmount, deferring destruction to survive React
|
|
* StrictMode's simulated unmount/re-mount cycle.
|
|
*
|
|
* StrictMode runs effects, then immediately runs cleanup, then re-runs
|
|
* effects — all synchronously. By deferring `destroy()` to a macrotask,
|
|
* the re-mount effect can cancel it before it fires.
|
|
*
|
|
* @param instance - Object with a `destroy()` method.
|
|
* @param setup - Optional setup called on first mount. Skipped on StrictMode
|
|
* re-mount since the previous setup was never torn down.
|
|
* @param teardown - Optional teardown called right before `destroy()` on real
|
|
* unmount. Skipped on StrictMode simulated unmount.
|
|
*/
|
|
export function useDestroy(instance: Destroyable, setup?: () => void, teardown?: () => void): void {
|
|
const pendingRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
const setupRef = useLatestRef(setup);
|
|
const teardownRef = useLatestRef(teardown);
|
|
|
|
useEffect(() => {
|
|
if (pendingRef.current !== null) {
|
|
clearTimeout(pendingRef.current);
|
|
pendingRef.current = null;
|
|
} else {
|
|
setupRef.current?.();
|
|
}
|
|
|
|
return () => {
|
|
pendingRef.current = setTimeout(() => {
|
|
teardownRef.current?.();
|
|
instance.destroy();
|
|
}, 0);
|
|
};
|
|
}, [instance]);
|
|
}
|