Input
Input refers to the data provided to a state machine that influences its behavior. In XState, you provide input when creating an actor using the second argument of the createActor(machine, { input })
function:
import { createActor, createMachine } from 'xstate';
const feedbackMachine = createMachine({
context: ({ input }) => ({
userId: input.userId,
feedback: '',
rating: input.defaultRating,
}),
// ...
});
const feedbackActor = createActor(feedbackMachine, {
input: {
userId: '123',
defaultRating: 5,
},
});
Creating actors with input​
You can pass input
to any kind of actor by reading this input from the input
property of the first argument to actor logic creators, such as fromPromise()
, fromTransition()
, fromObservable()
, etc.:
import { createActor, fromPromise } from 'xstate';
const userFetcher = fromPromise(({ input }) => {
return fetch(`/users/${input.userId}`).then((res) => res.json());
});
const userFetcherActor = createActor(userFetcher, {
input: {
userId: '123',
},
}).start();
userFetcherActor.onDone((data) => {
console.log(data);
// logs the user data for userId 123
});
Coming soon… show examples for fromTransition
, fromObservable
, etc.
Initial event input​
When an actor is started, it will automatically send a special event named xstate.init
to itself. If input
is provided to the createActor(logic, { input })
function, it will be included in the xstate.init
event:
import { createActor, createMachine } from 'xstate';
const feedbackMachine = createMachine({
entry: ({ event }) => {
console.log(event.input);
// logs { userId: '123', defaultRating: 5 }
},
// ...
});
const feedbackActor = createActor(feedbackMachine, {
input: {
userId: '123',
defaultRating: 5,
},
}).start();
Invoking actors with input​
You can provide input to invoked actors via the input
property of the invoke
configuration:
import { createActor, createMachine } from 'xstate';
const feedbackMachine = createMachine({
invoke: {
src: 'liveFeedback',
input: {
domain: 'stately.ai',
},
},
}).provide({
actors: {
liveFeedback: fromPromise(({ input }) => {
return fetch(`https://${input.domain}/feedback`).then((res) =>
res.json(),
);
}),
},
});
The invoke.input
property can be a static input value or a function that returns the input value. The function will be called with an object that contains the current context
and event
:
import { createActor, createMachine } from 'xstate';
const feedbackMachine = createMachine({
context: {
userId: '',
feedback: '',
rating: 0,
},
invoke: {
src: 'fetchUser',
input: ({ context }) => ({ userId: context.userId }),
},
// ...
}).provide({
actors: {
fetchUser: fromPromise(({ input }) => {
return fetch(`/users/${input.userId}`).then((res) => res.json());
}),
},
});
Spawning actors with input​
You can provide input to spawned actors via the input
property of the spawn
configuration:
import { createActor, createMachine } from 'xstate';
const feedbackMachine = createMachine({
context: {
userId: '',
feedback: '',
rating: 0,
emailRef: null,
},
// ...
on: {
'feedback.submit': {
actions: assign({
emailRef: ({ context, spawn }) => {
return spawn('emailUser', {
input: { userId: context.userId },
});
},
}),
},
},
// ...
}).provide({
actors: {
emailUser: fromPromise(({ input }) => {
return fetch(`/users/${input.userId}`, {
method: 'POST',
// ...
});
}),
},
});
Use-cases​
Input is useful for creating reusable machines that can be configured with different input values.
- Replaces the old way of writing a factory function for machines:
// Old way: using a factory function
const createFeedbackMachine = (userId, defaultRating) => {
return createMachine({
context: {
userId,
feedback: '',
rating: defaultRating,
},
// ...
});
};
const feedbackMachine1 = createFeedbackMachine('123', 5);
const feedbackActor1 = createActor(feedbackMachine1).start();
// New way: using input
const feedbackMachine = createMachine({
context: ({ input }) => ({
userId: input.userId,
feedback: '',
rating: input.defaultRating,
}),
// ...
});
const feedbackActor = createActor(feedbackMachine, {
input: {
userId: '123',
defaultRating: 5,
},
});
- Coming soon… Usage with
@xstate/react
- Coming soon… Usage with
@xstate/vue
- Coming soon… Usage with
@xstate/svelte
- Coming soon… Usage with
@xstate/solid
Passing new data to an actor​
Changing the input will not cause the actor to be restarted. You need to send an event to the actor to pass the new data to the actor:
const Component = (props) => {
const feedbackActor = useActor(feedbackMachine, {
input: {
userId: props.userId,
defaultRating: props.defaultRating,
},
});
useEffect(() => {
feedbackActor.send({
type: 'userId.change',
userId: props.userId,
});
}, [props.userId]);
// ...
};
TypeScript​
You can strongly type the input
of your machine in the types.input
property of the machine config.
import { createActor, createMachine } from 'xstate';
const machine = createMachine({
types: {} as {
input: {
userId: string;
defaultRating: number;
};
context: {
userId: string;
feedback: string;
rating: number;
};
},
context: ({ input }) => ({
userId: input.userId,
feedback: '',
rating: input.defaultRating,
}),
});
const actor = createActor(machine, {
input: {
userId: '123',
defaultRating: 5,
},
});
Cheatsheet​
Use our XState input cheatsheet below to get started quickly.
Providing input​
const feedbackActor = createActor(feedbackMachine, {
input: {
userId: '123',
defaultRating: 5,
},
});
Providing input to invoked actors​
const feedbackMachine = createMachine({
invoke: {
src: 'liveFeedback',
input: {
domain: 'stately.ai',
},
},
});
Providing dynamic input to invoked actors​
const feedbackMachine = createMachine({
context: {
userId: 'some-user-id',
},
invoke: {
src: 'fetchUser',
input: ({ context }) => ({ userId: context.userId }),
},
});
Providing input to spawned actors​
const feedbackMachine = createMachine({
context: {
userId: '',
},
// ...
on: {
'feedback.submit': {
actions: assign({
emailRef: ({ context, spawn }) => {
return spawn('emailUser', {
input: { userId: context.userId },
});
},
}),
},
},
// ...
});