usehooks
usehooks copied to clipboard
Request: useAsyncStatus - convert an async function into pending/loading/success/error states + a trigger.
The idea is instead of writing a component like:
export function MyComponent(props: {
doSomethingAsync: () => Promise<'a' | 'b' | 'c'>;
}) {
const [status, setStatus] = useState<PossibleStates<'a' | 'b' | 'c'>>({
state: 'pending',
});
return (
<div>
{status.state === 'loading' && 'Loading...'}
{status.state === 'error' && 'Error!'}
<button
onClick={async () => {
setStatus({ state: 'loading' });
try {
const result = await props.doSomethingAsync();
setStatus({ state: 'success', data: result });
} catch (err) {
setStatus({ state: 'error' });
}
}}
>
Click me
</button>
</div>
);
}
We would do this:
export function MyComponent(props: {
doSomethingAsync: () => Promise<'a' | 'b' | 'c'>;
}) {
const [trigger, status] = useAsyncStatus(props.doSomethingAsync);
return (
<div>
{status.state === 'loading' && 'Loading...'}
{status.state === 'error' && 'Error!'}
<button onClick={trigger}>Click me</button>
</div>
);
}
Some considerations
- Some users would rather use async functions that return errors rather than throw errors. eg.
type AsyncFunction<TArgs extends Array<unknown>, TData, TError> =
(...args: TArgs) => Promise<{data: TData} | {error: TError}>
- Further gold plating could allow for the hook to also provide for the various feedback rendering, eg:
export function MyComponent(props: {
doSomethingAsync: () => Promise<'a' | 'b' | 'c'>;
}) {
const [trigger, status, feedbackJsx] = useAsyncStatus(props.doSomethingAsync, {
loadingJsx: <span>Loading...</span>
errorJsx: <span>Error!</span>
});
return (
<div>
{feedbackJsx}
<button onClick={trigger}>Click me</button>
</div>
);
}