import { useEffect, useRef } from 'react'; import { cancelable, CancelablePromise } from './cancellable-promise.utils'; type EmptyCb = () => void; type EffectResult = EmptyCb | void; type AsyncEffect = () => EffectResult | Promise; const cleanup = (_res: EffectResult) => { if (typeof _res === 'function') { (_res as EmptyCb)?.(); } }; const action = (effect: AsyncEffect) => { const _res = effect(); let toCleanup: EffectResult; let toCancel: CancelablePromise; // check if its promise if ((_res as Promise)?.then) { toCancel = cancelable(_res as Promise).then( (v) => (toCleanup = v) ); } else { toCleanup = _res as EffectResult; } return () => { toCancel?.cancel(); cleanup(toCleanup); }; }; export const useAsyncEffect = (effect: AsyncEffect, deps: Array) => { useEffect(() => { return action(effect); // eslint-disable-next-line react-hooks/exhaustive-deps }, deps); }; export const useAsyncEffectOnConditionOnce = ( effect: AsyncEffect, deps: Array, condition: boolean ) => { const wasLaunched = useRef(false); useEffect(() => { if (condition && !wasLaunched.current) { wasLaunched.current = true; return action(effect); } return () => {}; // eslint-disable-next-line react-hooks/exhaustive-deps }, deps); };