Compare commits
No commits in common. "20c36f7c8b638f0b69b896cf2485c8b78d921cda" and "8904ed741c0cab41c00424b24eff52d1e30bc091" have entirely different histories.
20c36f7c8b
...
8904ed741c
2 changed files with 1 additions and 112 deletions
111
src/Async.tsx
111
src/Async.tsx
|
@ -1,111 +0,0 @@
|
||||||
import React, {useMemo, useState} from "react";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Asynchronous data state.
|
|
||||||
*/
|
|
||||||
interface AsyncState<T>
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Determine if we are waiting for the promise result or not.
|
|
||||||
*/
|
|
||||||
pending: boolean;
|
|
||||||
/**
|
|
||||||
* The promise which is retrieved (or has retrieved) data.
|
|
||||||
*/
|
|
||||||
promise: Promise<T>;
|
|
||||||
/**
|
|
||||||
* Error thrown by the promise.
|
|
||||||
*/
|
|
||||||
error: Error;
|
|
||||||
/**
|
|
||||||
* The promise result.
|
|
||||||
*/
|
|
||||||
data: T;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A promise production function.
|
|
||||||
*/
|
|
||||||
export type PromiseFn<T> = () => Promise<T>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* React hook for promise result retrieval.
|
|
||||||
* @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.
|
|
||||||
*/
|
|
||||||
export function useAsync<T>(promise: Promise<T>|PromiseFn<T>, deps: any[] = []): AsyncState<T>
|
|
||||||
{
|
|
||||||
// Get the actual promise from the function if there is one.
|
|
||||||
if ((promise as PromiseFn<T>)?.call)
|
|
||||||
promise = (promise as PromiseFn<T>)();
|
|
||||||
else
|
|
||||||
promise = Promise.race([promise as Promise<T>]);
|
|
||||||
|
|
||||||
// The async state.
|
|
||||||
const [state, setState] = useState<AsyncState<T>>({
|
|
||||||
pending: true,
|
|
||||||
promise: promise,
|
|
||||||
error: undefined,
|
|
||||||
data: undefined,
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Partial update of an async state.
|
|
||||||
* @param stateUpdate A partial update object.
|
|
||||||
*/
|
|
||||||
const updateState = (stateUpdate: Partial<AsyncState<T>>) => {
|
|
||||||
// Copy the original state and apply the partial state update.
|
|
||||||
setState(Object.assign({}, state, stateUpdate));
|
|
||||||
};
|
|
||||||
|
|
||||||
// Reconfigure the promise when any deps have changed.
|
|
||||||
useMemo(() => {
|
|
||||||
(promise as Promise<T>).then((result) => {
|
|
||||||
// When there is a result, disable pending state and set retrieved data, without error.
|
|
||||||
updateState({
|
|
||||||
pending: false,
|
|
||||||
error: undefined,
|
|
||||||
data: result,
|
|
||||||
})
|
|
||||||
}).catch((error) => {
|
|
||||||
// An error happened, disable pending state, reset data, and set the error.
|
|
||||||
updateState({
|
|
||||||
pending: false,
|
|
||||||
error: error,
|
|
||||||
data: undefined,
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
// Promise is ready: reset the state to pending with the configured promise, without data and error.
|
|
||||||
updateState({
|
|
||||||
pending: true,
|
|
||||||
promise: promise as Promise<T>,
|
|
||||||
error: undefined,
|
|
||||||
data: undefined,
|
|
||||||
});
|
|
||||||
}, deps);
|
|
||||||
|
|
||||||
return state; // Return the current async state.
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Wait for the promise to be fulfilled to render the children.
|
|
||||||
* @param async The async state.
|
|
||||||
* @param children Renderer function of the children, takes promised data as argument.
|
|
||||||
* @param fallback Content shown when the promise is not fulfilled yet.
|
|
||||||
* @constructor
|
|
||||||
*/
|
|
||||||
export function Await<T>({ async, children, fallback }: {
|
|
||||||
async: AsyncState<T>;
|
|
||||||
children: (async: T) => React.ReactElement;
|
|
||||||
fallback?: React.ReactElement;
|
|
||||||
})
|
|
||||||
{
|
|
||||||
// Still waiting for the promised data, showing fallback content.
|
|
||||||
if (async.pending) return fallback ?? <></>;
|
|
||||||
// An error happened, throwing it.
|
|
||||||
if (async.error) throw async.error;
|
|
||||||
|
|
||||||
// Promise is fulfilled, rendering the children with result data.
|
|
||||||
return children(async.data);
|
|
||||||
}
|
|
|
@ -214,7 +214,7 @@ export function Select<OptionKey extends keyof any, Option>(
|
||||||
<li key={String(optionKey)}>
|
<li key={String(optionKey)}>
|
||||||
<Check />
|
<Check />
|
||||||
<div className={"option"}>{(renderOption ?? defaultRenderOption)(option)}</div>
|
<div className={"option"}>{(renderOption ?? defaultRenderOption)(option)}</div>
|
||||||
<button className={"remove flat"} type={"button"} onMouseDown={() => handleDeselectedOption(optionKey)}>
|
<button className={"remove flat"} type={"button"} onClick={() => handleDeselectedOption(optionKey)}>
|
||||||
<X />
|
<X />
|
||||||
</button>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
|
|
Loading…
Reference in a new issue