useAsyncAction
One step a surface waits on. The button goes quiet while it runs, and the reason stays on screen when it fails.
tsx
import { useAsyncAction } from '@basmilius/react-ui';tsx
import { Button, FormError, useAsyncAction } from '@basmilius/react-ui';
const wait = (ms: number): Promise<void> => new Promise((resolve) => window.setTimeout(resolve, ms));
let attempts = 0;
async function publish(): Promise<void> {
await wait(900);
attempts += 1;
if (attempts % 2 === 1) {
throw new Error('The registry refused the upload: the version already exists.');
}
}
export default function UseAsyncActionDemo() {
const step = useAsyncAction('Publishing failed.');
return (
<div className="flex w-80 flex-col items-start gap-2">
<Button variant="primary" disabled={step.busy} onClick={() => void step.run(publish)}>
{step.busy ? 'Publishing...' : 'Publish'}
</Button>
{step.failure !== null && <FormError>{step.failure}</FormError>}
</div>
);
}The first press fails, the second goes through. run(work) clears the last failure, sets busy, awaits the work, and answers whether it went through, so the caller closes a dialog or steps on only on success:
tsx
const step = useAsyncAction();
async function save(): Promise<void> {
if (await step.run(() => api.save(draft))) {
onClose();
}
}A rejection becomes failure, the error's message through messageOf. The fallback you pass the hook names the step when the rejection carries no sentence of its own, such as a thrown string.
What it answers
| Member | Type | |
|---|---|---|
busy | boolean | True while run awaits. |
failure | string | null | The reason the last run failed. |
run(work) | (work: () => Promise<unknown>) => Promise<boolean> | |
fail(message) | (message: string) => void | A refusal the surface sees for itself, shown in the same line as one that came back. |
clear() | () => void |
PromptDialog runs its confirm through this hook. AsyncAction is an exported type.