You will notice a type error with invoke
:
Property 'input' is missing in type [...]
Since submitActor
requires an input we need to pass it when executing the actor.
Take a moment to notice how since XState v5 most properties are completely type safe by default! 💯
We pass the input by defining input
inside the invoke
object:
const machine = setup({
types: {
context: {} as Context,
events: {} as Event,
},
actors: { submitActor },
}).createMachine({
context: initialContext,
initial: "Editing",
states: {
Editing: {
on: {
"update-username": {
actions: assign(({ event }) => ({
username: event.username,
})),
},
submit: { target: "Loading" },
},
},
Loading: {
invoke: {
src: "submitActor",
input: ({ event, context }) => {
// Return the input for the submitActor
},
},
},
},
});
XState provides a helper function called assertEvent
.
assertEvent
will throw an error if the event is not of the expected type.
We use assertEvent
to verify the event as submit
, extract event
and return the input for the submitActor
:
const machine = setup({
types: {
context: {} as Context,
events: {} as Event,
},
actors: { submitActor },
}).createMachine({
context: initialContext,
initial: "Editing",
states: {
Editing: {
on: {
"update-username": {
actions: assign(({ event }) => ({
username: event.username,
})),
},
submit: { target: "Loading" },
},
},
Loading: {
invoke: {
src: "submitActor",
input: ({ event, context }) => {
// 👇 Only "submit" contains `event` required as input to the actor
assertEvent(event, "submit");
return { event: event.event, context };
},
},
},
},
});
Since any event may cause the transition to the "Loading" state, we need to explicitly check that the event is of type
submit
before extractingevent
and returning the input for thesubmitActor
.Otherwise, the only thing we can do is throwing an error.
The submit
event contains React.FormEvent<HTMLFormElement>
that we pass to the submitActor
as input.
type Event =
| { type: "update-username"; username: string }
| { type: "submit"; event: React.FormEvent<HTMLFormElement> };
const submitActor = fromPromise(
async ({
input,
}: {
input: { event: React.FormEvent<HTMLFormElement>; context: Context };
}) => {
input.event.preventDefault();
await postRequest(input.context);
}
);