Last active
April 28, 2022 04:28
-
-
Save kossnocorp/e9f8e738c21a0d605236167d8a55495d to your computer and use it in GitHub Desktop.
Promise hooks
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 { useEffect, useMemo, useRef } from 'preact/hooks' | |
/** | |
* The {@link usePromise} resolve function. | |
*/ | |
export type UsePromiseResolve<Type> = (value: Type) => void | |
/** | |
* The {@link usePromise} reject function. | |
*/ | |
export type UsePromiseReject = (error: unknown) => void | |
/** | |
* The {@link usePromise} hook result. | |
*/ | |
export interface UsePromiseResult<Type> { | |
/** The promise to the value of given type */ | |
promise: Promise<Type> | |
/** The function that resolve the promise with the given value */ | |
resolve: UsePromiseResolve<Type> | |
/** The function that rejects the promise with the given reason */ | |
reject: UsePromiseReject | |
} | |
/** | |
* A hook that creates promise components - the promise itself, and resolve and | |
* reject functions that can be used to fulfill the promise. | |
* | |
* @returns promise components | |
*/ | |
export function usePromise<Type>(): UsePromiseResult<Type> { | |
const valueRef = useRef<Type>() | |
const errorRef = useRef<unknown>() | |
const resolveRef = useRef<UsePromiseResolve<Type>>() | |
const rejectRef = useRef<UsePromiseReject>() | |
const promise = useMemo( | |
() => | |
new Promise<Type>((promiseResolve, promiseReject) => { | |
if (valueRef.current) { | |
// If the value is already there, resolve it | |
promiseResolve(valueRef.current) | |
} else { | |
// Otherwise, save the resolve function | |
resolveRef.current = promiseResolve | |
} | |
if (errorRef.current) { | |
// If the error is already there, reject it | |
promiseReject(errorRef.current) | |
} else { | |
// Otherwise, save the reject function | |
rejectRef.current = promiseReject | |
} | |
}), | |
[] | |
) | |
const resolve: UsePromiseResolve<Type> = useMemo( | |
() => (value) => { | |
if (resolveRef.current) { | |
// If the resolve function is already there, use it | |
resolveRef.current(value) | |
} else { | |
// Otherwise, save the value | |
valueRef.current = value | |
} | |
}, | |
[] | |
) | |
const reject: UsePromiseReject = useMemo( | |
() => (error) => { | |
if (rejectRef.current) { | |
// If the reject function is already there, use it | |
rejectRef.current(error) | |
} else { | |
// Otherwise, save the error | |
errorRef.current = error | |
} | |
}, | |
[] | |
) | |
return { promise, resolve, reject } | |
} | |
/** | |
* The {@link usePromiseFrom} hook result. | |
*/ | |
export interface UsePromiseFromResult<Type> { | |
/** The promise to the value of given type */ | |
promise: Promise<Type> | |
/** The value if the promise is resolved */ | |
current: Type | undefined | null | |
/** The error if the promise is reject */ | |
error: Error | undefined | |
} | |
/** | |
* A hook that creates promise from the given value. If the value is defined and | |
* not an error, the promise will resolve and the hook will have the value | |
* defined. If the value is an error, the promise will reject and the hook will | |
* have the error defined. | |
* | |
* @param value - the value to create promise from | |
* @returns the promise to the value as well as the value or error | |
*/ | |
export function usePromiseFrom<Type>( | |
value: Type | Error | undefined | null | |
): UsePromiseFromResult<Type> { | |
const { promise, resolve, reject } = usePromise<Type>() | |
useEffect(() => { | |
if (!value) return | |
if (value instanceof Error) { | |
reject(value) | |
} else { | |
// We can safely call the resolve function multiple times as only | |
// he first value will be resolved | |
resolve(value) | |
} | |
}, [value]) | |
const isError = value instanceof Error | |
// Exclude the error from the value, so the value is either of the given | |
// type or undefined | |
const current = isError ? undefined : value | |
// Set error only if the value is an error | |
const error = isError ? value : undefined | |
return { promise, current, error } | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment