import { useEffect, useState } from "react"; export type AsyncResult = { isLoading: boolean, value: T | undefined, error: TError | undefined }; export type AsyncState = [AsyncResult, (promise: Promise | undefined) => void] export function useAsync(supplier: Promise | (() => Promise) | undefined): AsyncResult { const [isLoading, setLoading] = useState(true); const [error, setError] = useState(undefined); const [value, setValue] = useState(undefined); const [semaphore] = useState<{ value: number }>({ value: 0 }) const [promise, setPromise] = useState(typeof supplier === "function" ? null : supplier) useEffect(() => { setLoading(true); setError(undefined); setValue(undefined); const myMagicNumber = semaphore.value + 1; semaphore.value = myMagicNumber; promise && promise.then(value => { if (semaphore.value == myMagicNumber) { setValue(value); setLoading(false); } }).catch(error => { console.error(error) if (semaphore.value == myMagicNumber) { setError(error); setLoading(false); } }) }, [ promise ]) useEffect(() => { if (typeof supplier === "function") { setPromise(supplier()); } else { setPromise(supplier); } }, [ supplier ]) return { isLoading, value, error, }; } export function useAsyncState(initial: Promise | undefined): AsyncState { const [promise, setPromise] = useState | undefined>(initial); const asyncState = useAsync(promise); return [ asyncState, setPromise ]; }