Add Promisable type and allow it as an argument to useAsync.
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful

This commit is contained in:
Madeorsk 2024-07-19 21:48:28 +02:00
parent 76182885db
commit f1f8420de8
Signed by: Madeorsk
SSH key fingerprint: SHA256:J9G0ofIOLKf7kyS2IfrMqtMaPdfsk1W02+oGueZzDDU

View file

@ -1,5 +1,10 @@
import React, {useEffect, useState} from "react"; import React, {useEffect, useState} from "react";
/**
* A type that can be returned by a promise or as is.
*/
export type Promisable<T> = T|Promise<T>;
/** /**
* Asynchronous data state. * Asynchronous data state.
*/ */
@ -12,7 +17,7 @@ interface AsyncState<T>
/** /**
* The promise which is retrieved (or has retrieved) data. * The promise which is retrieved (or has retrieved) data.
*/ */
promise: Promise<T>; promise: Promisable<T>;
/** /**
* Error thrown by the promise. * Error thrown by the promise.
*/ */
@ -33,20 +38,20 @@ export type PromiseFn<T> = () => Promise<T>;
* @param promise The promise or a function that produces a promise. * @param promise The promise or a function that produces a promise.
* @param deps When one of the `deps` change, it will wait for the promise again. * @param deps When one of the `deps` change, it will wait for the promise again.
*/ */
export function useAsync<T>(promise: Promise<T>|PromiseFn<T>, deps: any[] = []): AsyncState<T> export function useAsync<T>(promise: Promisable<T>|PromiseFn<T>, deps: any[] = []): AsyncState<T>
{ {
// Get the actual promise from the function if there is one. // Get the actual promise from the function if there is one.
if ((promise as PromiseFn<T>)?.call) if ((promise as PromiseFn<T>)?.call)
promise = (promise as PromiseFn<T>)(); promise = (promise as PromiseFn<T>)();
else else if (promise instanceof Promise)
promise = Promise.race([promise as Promise<T>]); promise = Promise.race([promise as Promise<T>]);
// The async state. // The async state.
const [state, setState] = useState<AsyncState<T>>({ const [state, setState] = useState<AsyncState<T>>({
pending: true, pending: promise instanceof Promise,
promise: promise, promise: promise as Promisable<T>,
error: undefined, error: undefined,
data: undefined, data: promise instanceof Promise ? undefined : promise as T,
}); });
/** /**
@ -60,7 +65,11 @@ export function useAsync<T>(promise: Promise<T>|PromiseFn<T>, deps: any[] = []):
// Reconfigure the promise when any deps have changed. // Reconfigure the promise when any deps have changed.
useEffect(() => { useEffect(() => {
(promise as Promise<T>).then((result) => { if (!(promise instanceof Promise))
// If it's not a promise, there is nothing to wait for.
return;
promise.then((result) => {
// When there is a result, disable pending state and set retrieved data, without error. // When there is a result, disable pending state and set retrieved data, without error.
updateState({ updateState({
pending: false, pending: false,
@ -79,7 +88,7 @@ export function useAsync<T>(promise: Promise<T>|PromiseFn<T>, deps: any[] = []):
// Promise is ready: reset the state to pending with the configured promise, without data and error. // Promise is ready: reset the state to pending with the configured promise, without data and error.
updateState({ updateState({
pending: true, pending: true,
promise: promise as Promise<T>, promise: promise,
error: undefined, error: undefined,
data: undefined, data: undefined,
}); });