XState v6 alpha
Handle async requests
Model loading, success and failure states for an async request.
XState v6 is in alpha
APIs and behavior may change before the stable release.
Create async actor logic, then invoke it from a state.
import { createActor, createAsyncLogic, createMachine } from 'xstate';
const search = createAsyncLogic({
run: async ({ input, signal }: { input: { query: string } }) => {
const response = await fetch(`/api/search?q=${input.query}`, { signal });
if (!response.ok) throw new Error('Search failed');
return response.json() as Promise<string[]>;
}
});
const searchMachine = createMachine({
context: { query: '', results: [] as string[], error: null as unknown },
initial: 'idle',
states: {
idle: {
on: {
search: ({ context, event }) => ({
target: 'loading',
context: { ...context, query: event.query }
})
}
},
loading: {
invoke: {
src: search,
input: ({ context }) => ({ query: context.query }),
onDone: ({ context, event }) => ({
target: 'success',
context: { ...context, results: event.output, error: null }
}),
onError: ({ context, event }) => ({
target: 'failure',
context: { ...context, error: event.error }
})
}
},
success: { on: { search: { target: 'loading' } } },
failure: { on: { retry: { target: 'loading' } } }
}
});
const actor = createActor(searchMachine).start();
actor.send({ type: 'search', query: 'actors' });The request starts when the machine enters loading. It stops when the machine leaves loading.
The same shape works for loading a user profile or checking inventory before checkout. Keep request input in context when retry needs the same values.