Actions
액션은 실행 후 잊어버리는 효과(fire-and-forget effect)이다. 상태머신이 트랜지션될 때, 액션을 실행할 수 있다. 액션은 이벤트에 대한 응답으로 발생하며, 일반적으로 actions: [...]
프로퍼티로 정의된다. state에서 기재될 수 있는 모든 트랜지션에 대해 액션을 정의할 수 있다. 시작, 종료 프로퍼티에도 가능하다.
import { setup } from 'xstate';
const feedbackMachine = setup({
actions: {
track: (_, params: unknown) => {
track(params);
// Tracks { response: 'good' }
},
showConfetti: () => {
// ...
}
}
}).createMachine({
// ...
states: {
// ...
question: {
on: {
'feedback.good': {
actions: [
{ type: 'track', params: { response: 'good' } }
]
}
},
exit: ['exitAction']
}
thanks: {
entry: ['showConfetti'],
}
}
});
actions 예시:
- 메세지 로깅
- 다른 actor로 메세지 전달
- context 업데이트
Action objects
액션 객체는 type과 선택적인 params를 가진다:
- params 프로퍼티는 작업과 관련된 매개변수 값이 저장된다.
import { setup } from "xstate";
const feedbackMachine = setup({
actions: {
track: (_, params: unknown) => {
/* ... */
},
},
}).createMachine({
// ...
states: {
// ...
question: {
on: {
"feedback.good": {
actions: [
{
// Action type
type: "track",
// Action params
params: { response: "good" },
},
],
},
},
},
},
});
Dynamic action parameters
params를 리턴하는 함수를 사용하여 동적으로 파라미터값을 params
프로퍼티에 전달할 수 있다. 이 함수는 context와 event를 가지고 있는 객체를 인자로 가진다.
import { setup } from "xstate";
const feedbackMachine = setup({
actions: {
logInitialRating: (_, params: { initialRating: number }) => {
// ...
},
},
}).createMachine({
context: {
initialRating: 3,
},
entry: [
{
type: "logInitialRating",
params: ({ context }) => ({
initialRating: context.initialRating,
}),
},
],
});
아래 방식은 재사용이 가능하고 머신의 context나 event 타입에 의존하지 않는 정의방법으로 권장된다.
import { setup } from "xstate";
function logInitialRating(_, params: { initialRating: number }) {
console.log(`Initial rating: ${params.initialRating}`);
}
const feedbackMachine = setup({
actions: { logInitialRating },
}).createMachine({
context: { initialRating: 3 },
entry: [
{
type: "logInitialRating",
params: ({ context }) => ({
initialRating: context.initialRating,
}),
},
],
});
Inline actions
import { createMachine } from "xstate";
const feedbackMachine = createMachine({
entry: [
// Inline action
({ context, event }) => {
console.log(/* ... */);
},
],
});
액션 객체를 사용하는 것이 권장된다.
Implementing actions
// 방법 1
import { setup } from "xstate";
const feedbackMachine = setup({
actions: {
track: ({ context, event }, params) => {
// Action implementation
// ...
},
},
}).createMachine({
// Machine config
entry: [{ type: "track", params: { msg: "entered" } }],
});
// 방법 2
const feedbackActor = createActor(
feedbackMachine.provide({
actions: {
track: ({ context, event }, params) => {
// Different action implementation
// (overrides previous implementation)
// ...
},
},
})
);
Assign action
assign()
액션은 context에 데이터를 할당하는 특수한 액션이다. assign(assignment)
의 assignments 인수는 context에 대한 할당이 지정되는 곳이다.
import { setup } from "xstate";
const countMachine = setup({
types: {
events: {} as { type: "increment"; value: number },
},
}).createMachine({
context: {
count: 0,
},
on: {
increment: {
actions: assign({
count: ({ context, event }) => context.count + event.value,
}),
},
},
});
const countActor = createActor(countMachine);
countActor.subscribe((state) => {
console.log(state.context.count);
});
countActor.start();
// logs 0
countActor.send({ type: "increment", value: 3 });
// logs 3
countActor.send({ type: "increment", value: 2 });
// logs 5
Raise action
raise 액션은 동일한 머신에서 받아온 이벤트를 발생하는 특수한 액션이다. 이벤트 발생은 머신이 스스로에게 이벤트를 ‘send’할 수 있는 방법이다:
import { createMachine, raise } from 'xstate';
const machine = createMachine({
// ...
entry: raise({ type: 'someEvent', data: 'someData' });
});
내부적으로 이벤트다 raise되면, internal event queue 내부 이벤트 대기열에 배치된다. 현재 트랜지션이 완료된 이후에 이 이벤트들은 FIFO 순서대로 처리된다. 외부 이벤트는 내부 이벤트 큐의 모든 이벤트가 처리된 이후에만 처리된다.
raised 이벤트는 동적일 수도 있다:
import { createMachine, raise } from "xstate";
const machine = createMachine({
// ...
entry: raise(({ context, event }) => ({
type: "dynamicEvent",
data: context.someValue,
})),
});
delay로도 사용될 수 있으며, 내부 이벤트 대기열에 넣지 않고 delay시켜서 발생시킬 수도 있다.
Send-to action
sendTo(...)
액션은 특정 액터에게 이벤트를 보내는 특수한 액션이다.
const machine = createMachine({
on: {
transmit: {
actions: sendTo("someActor", { type: "someEvent" }),
},
},
});
// dynamic
const machine = createMachine({
on: {
transmit: {
actions: sendTo("someActor", ({ context, event }) => {
return { type: "someEvent", data: context.someData };
}),
},
},
});
// 도착지 actor는 actor ID 또는 스스로를 참조하는 actor가 될 수 있다.
const machine = createMachine({
context: ({ spawn }) => ({
someActorRef: spawn(fromPromise(/* ... */)),
}),
on: {
transmit: {
actions: sendTo(({ context }) => context.someActorRef, {
type: "someEvent",
}),
},
},
});
// delay와 id 같은 다른 옵션을 세번째 인자에 전달할 수 있다.
const machine = createMachine({
on: {
transmit: {
actions: sendTo(
"someActor",
{ type: "someEvent" },
{
id: "transmission",
delay: 1000,
}
),
},
},
});
Enqueue actions
enqueueActions(...)
액션 생성자는 액션을 실제로 실행하지 않고 순차적으로 실행할 액션을 대기열에 추가하는 상위 레벨 액션이다. 이 함수는 context, event를 콜백으로 받을 수 있고, 더해서 enqueue, check 함수를 수신할 수 있다:
enqueue(...)
함수는 액션을 대기열에 추가하는 데에 사용된다. 이 함수는 액션 객체 또는 액션 함수를 받는다:
actions: enqueueActions(({ enqueue }) => {
// Enqueue an action object
enqueue({ type: "greet", params: { message: "hi" } });
// Enqueue an action function
enqueue(() => console.log("Hello"));
// Enqueue a simple action with no params
enqueue("doSomething");
});
check(...)
함수는 조건적으로 액션을 대기열에 추가하는 데에 사용된다. guard 객체 또는 guard 함수를 받아서 이 가드가 true인지 여부를 나타내는 boolean을 리턴한다.
actions: enqueueActions(({ enqueue, check }) => {
if (check({ type: "everythingLooksGood" })) {
enqueue("doSomething");
}
});
- 기본 빌트인 액션으로 헬퍼메소드가
enqueue
에 있다: enqueue.assign()
enqueue.sendTo()
enqueue.raise()
enqueue.spawnChild()
enqueue.stopChild()
enqueue.cancel()
대기열에 추가된 액션은 조건부로 호출할 수 있지만, 비동기적으로 대기열에 추가할 수 없다.
const machine = createMachine({
// ...
entry: enqueueActions(({ context, event, enqueue, check }) => {
// assign action
enqueue.assign({
count: context.count + 1,
});
// Conditional actions (replaces choose(...))
if (event.someOption) {
enqueue.sendTo("someActor", { type: "blah", thing: context.thing });
// other actions
enqueue("namedAction");
// with params
enqueue({ type: "greet", params: { message: "hello" } });
} else {
// inline
enqueue(() => console.log("hello"));
// even built-in actions
}
// Use check(...) to conditionally enqueue actions based on a guard
if (check({ type: "someGuard" })) {
// ...
}
// no return
}),
});
Log action
log(...)
액션은 콘솔에 로그메세지를 간단하게 남길 수 있다.
import { createMachine, log } from "xstate";
const machine = createMachine({
on: {
someEvent: {
actions: log("some message"),
},
},
});
Cancel action
cancel(...)
액션은 지연된 sentTo(...)
또는 raise(...)
액션을 그들의 IDs로 취소한다.
import { createMachine, sendTo, cancel } from "xstate";
const machine = createMachine({
on: {
event: {
actions: sendTo(
"someActor",
{ type: "someEvent" },
{
id: "someId",
delay: 1000,
}
),
},
cancelEvent: {
actions: cancel("someId"),
},
},
});
Stop child action
stopChild(...)
액션은 자식 액터를 멈춘다. 액터는 그들의 부모 액터를 통해서 멈출 수 있다:
import { createMachine, stopChild } from "xstate";
const machine = createMachine({
context: ({ spawn }) => ({
spawnedRef: spawn(fromPromise(/* ... */), { id: "spawnedId" }),
}),
on: {
stopById: {
actions: stopChild("spawnedId"),
},
stopByRef: {
actions: stopChild(({ context }) => context.spawnedRef),
},
},
});
Modeling
이벤트에 대한 응답으로 액션이 실행해야하는 경우, 액션만 있는 self-transition을 만들 수 있다.
import { createMachine } from "xstate";
const countMachine = createMachine({
context: {
count: 0,
},
on: {
increment: {
actions: assign({
count: ({ context, event }) => context.count + event.value,
}),
},
decrement: {
actions: assign({
count: ({ context, event }) => context.count - event.value,
}),
},
},
});