XState v6 alpha
Async logic
Model loading, success and failure with invoked actors.
Async work has behavior before and after a promise settles. Model those parts as states.
import { createActor, createAsyncLogic, createMachine, waitFor } from 'xstate';
const loadUser = createAsyncLogic({
run: async ({ input }: { input: { id: number } }) => {
const response = await fetch(`/api/users/${input.id}`);
if (!response.ok) throw new Error('Could not load user');
return response.json() as Promise<{ id: number; name: string }>;
}
});
const userMachine = createMachine({
context: { user: null as { id: number; name: string } | null },
initial: 'idle',
states: {
idle: { on: { load: { target: 'loading' } } },
loading: {
invoke: {
src: loadUser,
input: { id: 42 },
onDone: ({ event }) => ({
target: 'success',
context: { user: event.output }
}),
onError: { target: 'failure' }
}
},
success: {},
failure: { on: { retry: { target: 'loading' } } }
}
});
const user = createActor(userMachine).start();
user.send({ type: 'load' });
await waitFor(user, (snapshot) => snapshot.matches('success'));Entering loading starts the invoked actor. Leaving loading stops it.
The done event contains the actor's output. The error event contains the thrown error. The signal passed to async logic is aborted when the actor stops, so pass it to fetch and other abortable APIs.
Real examples
- Invoke payment authorization while an order is
authorizing. Move toconfirmed,declinedortimedOutfrom the result. - Invoke a file upload while a file is
uploading. Acancelevent leaves that state and aborts the request.
Model retries as states and delayed transitions when users or operators need to see and control them.