Created
August 6, 2024 05:07
-
-
Save buYoung/890ecd4fcecd4d23e8f354b1b3988479 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import type { DependencyList } from 'react'; | |
import { useCallback, useEffect, useRef } from 'react'; | |
type EffectCallback = () => void | Promise<void> | Generator<any, void, any> | (() => void); | |
// 타입 가드 함수들 | |
function isGenerator(obj: any): obj is Generator { | |
return obj && typeof obj.next === 'function' && typeof obj.throw === 'function'; | |
} | |
function isPromise<T>(obj: any): obj is Promise<any> { | |
return obj && typeof obj.then === 'function'; | |
} | |
function isFunction(obj: any): obj is Function { | |
return typeof obj === 'function'; | |
} | |
type Cleanup = () => void; | |
function useAsyncEffect(effect: EffectCallback | (() => void), deps?: DependencyList): void { | |
const isMountedRef = useRef(true); | |
const isExecutingRef = useRef(false); | |
const latestEffectRef = useRef<EffectCallback>(effect); | |
const cleanupRef = useRef<Cleanup | undefined>(); | |
// 최신 effect 함수를 ref에 저장 | |
latestEffectRef.current = effect; | |
const executeEffect = useCallback(async () => { | |
if (isExecutingRef.current) return; | |
isExecutingRef.current = true; | |
try { | |
if (cleanupRef.current) { | |
cleanupRef.current(); | |
cleanupRef.current = undefined; | |
} | |
const result = latestEffectRef.current(); | |
if (isGenerator(result)) { | |
// Generator 처리 | |
const generator = result; | |
let res = generator.next(); | |
while (!res.done) { | |
if (!isMountedRef.current) return; | |
res = generator.next(await res.value); | |
} | |
if (isMountedRef.current && typeof res.value === 'function') { | |
cleanupRef.current = res.value; | |
} | |
} else if (isPromise(result)) { | |
// Promise 처리 | |
const cleanup = await result; | |
if (isMountedRef.current && typeof cleanup === 'function') { | |
cleanupRef.current = cleanup; | |
} | |
} else if (typeof result === 'function') { | |
// 동기 cleanup 함수 처리 | |
cleanupRef.current = result; | |
} | |
} catch (error) { | |
console.error('Error in useAsyncEffect:', error); | |
} finally { | |
isExecutingRef.current = false; | |
} | |
}, []); | |
useEffect(() => { | |
executeEffect(); | |
return () => { | |
isMountedRef.current = false; | |
if (cleanupRef.current) { | |
cleanupRef.current(); | |
} | |
}; | |
}, deps); | |
} | |
export default useAsyncEffect; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment