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, setup } from 'xstate';
const feedbackMachine = setup({
types: {
context: {} as {
userId: string;
feedback: string;
rating: number;
},
input: {} as {
userId: string;
defaultRating: number;
},
},
}).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()
, and other actor logic creators.
Input with fromPromise()
:
import { createActor, fromPromise } from 'xstate';
const userFetcher = fromPromise(({ input }: { input: { userId: string } }) => {
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
});
Input with fromTransition()
:
import { createActor, fromTransition } from 'xstate';
const counter = fromTransition((state, event)) => {
if (event.type === 'INCREMENT') {
return { count: state.count + 1 };
}
return state;
}, ({ input }: { input: { startingCount?: number } }) => ({
count: input.startingCount ?? 0,
});
const counterActor = createActor(counter, {
input: {
startingCount: 10,
}
});
Input with fromObservable()
:
import { createActor, fromObservable } from 'xstate';
import { interval } from 'rxjs';
const intervalLogic = fromObservable(
({ input }: { input: { interval: number } }) => {
return interval(input.interval);
},
);
const intervalActor = createActor(intervalLogic, {
input: {
interval: 1000,
},
});
intervalActor.start();
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, setup } from 'xstate';
const feedbackMachine = setup({
actors: {
liveFeedback: fromPromise(({ input }: { input: { domain: string } }) => {
return fetch(`https://${input.domain}/feedback`).then((res) =>
res.json(),
);
}),
},
}).createMachine({
invoke: {
src: 'liveFeedback',
input: {
domain: 'stately.ai',
},
},
});
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, setup } from 'xstate';
const feedbackMachine = setup({
actors: {
fetchUser: fromPromise(({ input }) => {
return fetch(`/users/${input.userId}`).then((res) => res.json());
}),
},
}).createMachine({
context: {
userId: '',
feedback: '',
rating: 0,
},
invoke: {
src: 'fetchUser',
input: ({ context }) => ({ userId: context.userId }),
},
// ...
});
Spawning actors with inputβ
You can provide input to spawned actors via the input
property of the spawn
configuration:
import { createActor, setup, type AnyActorRef } from 'xstate';
const feedbackMachine = setup({
types: {
context: {} as {
userId: string;
feedback: string;
rating: number;
emailRef: AnyActorRef;
},
},
actors: {
emailUser: fromPromise(({ input }: { input: { userId: string } }) => {
return fetch(`/users/${input.userId}`, {
method: 'POST',
// ...
});
}),
},
}).createMachine({
context: {
userId: '',
feedback: '',
rating: 0,
emailRef: null,
},
// ...
on: {
'feedback.submit': {
actions: assign({
emailRef: ({ context, spawn }) => {
return spawn('emailUser', {
input: { userId: context.userId },
});
},
}),
},
},
// ...
});
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,
},
});
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]);
// ...
};
Input and TypeScriptβ
You can strongly type the input
of your machine in the types.input
property of the machine setup.
import { createActor, setup } from 'xstate';
const machine = setup({
types: {
input: {} as {
userId: string;
defaultRating: number;
};
context: {} as {
userId: string;
feedback: string;
rating: number;
};
},
}).createMachine({
context: ({ input }) => ({
userId: input.userId,
feedback: '',
rating: input.defaultRating,
}),
});
const actor = createActor(machine, {
input: {
userId: '123',
defaultRating: 5,
},
});
Input cheatsheetβ
Use our XState input cheatsheet below to get started quickly.
Cheatsheet: providing inputβ
const feedbackActor = createActor(feedbackMachine, {
input: {
userId: '123',
defaultRating: 5,
},
});
Cheatsheet: providing input to invoked actorsβ
const feedbackMachine = createMachine({
invoke: {
src: 'liveFeedback',
input: {
domain: 'stately.ai',
},
},
});
Cheatsheet: providing dynamic input to invoked actorsβ
const feedbackMachine = createMachine({
context: {
userId: 'some-user-id',
},
invoke: {
src: 'fetchUser',
input: ({ context }) => ({ userId: context.userId }),
},
});
Cheatsheet: providing dynamic input from event properties to invoked actorsβ
const feedbackMachine = createMachine({
types: {
events:
| { type: 'messageSent', message: string }
| { type: 'incremented', count: number },
},
invoke: {
src: 'fetchUser',
input: ({ event }) => {
assertEvent(event, 'messageSent');
return {
message: event.message,
};
},
},
});
Cheatsheet: 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 },
});
},
}),
},
},
// ...
});