https://xstate.js.org/docs/guides/effects.html
- 액션 : 단일, 이산 효과
- 액티비티 : 상태가 종료될 때 처리되는 연속 효과
- Actor로 대체
Invoked Effect(호출된 효과) :이벤트를 비동기적으로 보내고 받을 수 있는 사이드 이펙트를 실행합니다.
- Invoked Promises - 한 번 resolved되거나 rejected될 수 있는 시간 경과에 따른 단일 개별 효과, 이벤트로 상위 시스템에 전송됩니다.
- Invoked Callbacks - 시간이 지남에 따라 여러 이벤트를 보낼 수 있는 지속적인 효과, 상위 시스템으로/에서 직접 전송된 이벤트를 수신/ 대기할 수 있습니다.
- Invoked Observables - Ovserved된 스트림의 메시지에 의해 트리거되는 여러 개의 이벤트를 보낼 수 있는, 시간 경과에 따른 지속적인 효과
- RxJs와 활용
- 해당 스펙에 대한 공식 제안이 있음
- Invoked Machines - 이벤트를 보내고 받을 수 있으며, 최종 상태에 도달했을 때 상위 시스템에 알릴 수 있는 Machine 인스턴스로 표현되는 연속 효과
Actions (액션)
- entry 동작은 상태에 진입할 때 실행됩니다.
- exit 동작은 상태를 종료할 때 실행됩니다.
- transition이 수행될 때 전환 액션이 실행됩니다.
const triggerMachine = createMachine(
{
id: 'trigger',
initial: 'inactive',
states: {
inactive: {
on: {
TRIGGER: {
target: 'active',
// transition actions
actions: ['activate', 'sendTelemetry']
}
}
},
active: {
// entry actions
entry: ['notifyActive', 'sendTelemetry'],
// exit actions
exit: ['notifyInactive', 'sendTelemetry'],
on: {
STOP: { target: 'inactive' }
}
}
}
},
{
actions: {
// action implementations
activate: (context, event) => {
console.log('activating...');
},
notifyActive: (context, event) => {
console.log('active!');
},
notifyInactive: (context, event) => {
console.log('inactive!');
},
sendTelemetry: (context, event) => {
console.log('time:', Date.now());
}
}
}
);
transition vs entry/exit
entry/exit 액션은 "이 상태에 들어가거나 나가는 모든 전환에서 이 액션을 실행"을 의미합니다.
액션이 이전/다음 상태 노드 또는 이벤트가 아닌 해당 상태 노드에만 종속되는 경우 진입/종료 액션을 사용합니다.
// ...
{
idle: {
on: {
LOAD: 'loading'
}
},
loading: {
// this action is executed whenever the 'loading' state is entered
entry: 'fetchData'
}
}
// ...
// ...
{
idle: {
on: {
LOAD: {
target: 'loading',
// this action is executed only on this transition
actions: 'fetchData'
}
},
loading: {
// ...
}
}
// ...
TIP
// ...
TRIGGER: {
target: 'active',
actions: (context, event) => { console.log('activating...'); }
}
// ...
Declarative actions (선언적 액션)
const activeState = triggerMachine.transition('inactive', { type: 'TRIGGER' });
console.log(activeState.actions);
// [
// { type: 'activate', exec: ... },
// { type: 'sendTelemetry', exec: ... },
// { type: 'notifyActive', exec: ... },
// { type: 'sendTelemetry', exec: ... }
// ]
- type - the action 타입
- exec - the action i구현 함수
Argument | Type | Description |
context | TContext |
현재 머신 컨텍스트
|
event | event object |
전환을 일으킨 이벤트
|
actionMeta | meta object |
액션에 대한 메타 데이터를 포함하는 개체(아래 참조)
|
Property | Type | Description |
action | action object | 원래 액션 객체 |
state | State |
전환 후 resolved된 머신 상태
|
인터프리터는 currentState.context, event및 머신이 전환된 state를 사용하여 exec 함수를 호출합니다.
이 동작을 사용자 지정할 수 있습니다. 자세한 내용은executing actions 을 참조하세요.
Action order (액션 실행 순서)
- exit 액션 - 자식 상태로 전환한 상태 노드의 모든 종료 액션
- transition 액션 - 선택한 전환(부모,자식 순서쌍)에 정의된 모든 액션
- entry 액션 - 부모 상태에서 진입한 상태 노드의 모든 entry 액션
경고
XState 버전 4.x에서 assign action은 우선 순위를 가지며 다른 action보다 먼저 실행됩니다.
이 동작은 버전 5에서 순서대로 호출되도록 수정됩니다.
경고
여기에 문서화된 모든 액션 생성자는 액션 개체를 반환합니다.
액션 객체만 반환하고 이벤트를 명령적으로 보내지 않는 순수 함수입니다.
액션 생성자를 명령적으로 호출하지 마십시오. 그들은 아무것도 하지 않을 것입니다!
// 🚫 Do not do this!
entry: () => {
// 🚫 This will do nothing; send() is not an imperative function!
send({ type: 'SOME_EVENT' });
};
console.log(send({ type: 'SOME_EVENT' }));
// => { type: 'xstate.send', event: { type: 'SOME_EVENT' } }
// ✅ Do this instead
entry: send({ type: 'SOME_EVENT' });
Send action (액션 전송)
send(event)
Argument | Type | Description |
event | string or event object or event expression | The event to send to the specified options.to (or self) |
options? | send options (see below) | Options for sending the event. |
Property | Type | Description |
id? | string | The send ID (used for cancellation) |
to? | string | The target of the event (defaults to self) |
delay? | number | The timeout (milliseconds) before sending the event, if the event is not canceled before the timeout |
import { createMachine, send } from 'xstate';
const lazyStubbornMachine = createMachine({
id: 'stubborn',
initial: 'inactive',
states: {
inactive: {
on: {
TOGGLE: {
target: 'active',
// send the TOGGLE event again to the service
actions: send('TOGGLE')
}
}
},
active: {
on: {
TOGGLE: { target: 'inactive' }
}
}
}
});
const nextState = lazyStubbornMachine.transition('inactive', {
type: 'TOGGLE'
});
nextState.value;
// => 'active'
nextState.actions;
// => [{ type: 'xstate.send', event: { type: 'TOGGLE' }}]
// The service will proceed to send itself the { type: 'TOGGLE' } event.
- 문자열 이벤트, 예: send('TOGGLE')
- 이벤트 객체, 예: send({ type: 'TOGGLE', payload: ... })
- 현재 컨텍스트와 send() 액션을 트리거한 이벤트를 가져와 이벤트 객체를 반환하는 함수인 이벤트 표현식:
import { send } from 'xstate';
// contrived example - reads from the `context` and sends
// the dynamically created event
const sendName = send((context, event) => ({
type: 'NAME',
name: context.user.name
}));
const machine = createMachine({
// ...
on: {
TOGGLE: {
actions: sendName
}
}
//...
});
Send targets
send(...) 액션 생성자가 보낸 이벤트는 호출된 서비스(invoked services) 또는 생성된 액터(spawned actors)와 같은 특정 대상으로 보내야 함을 나타낼 수 있습니다.
이것은 send(...) 액션에서 { to: ... } 속성을 지정하여 수행됩니다.
// ...
invoke: {
id: 'some-service-id',
src: 'someService',
// ...
},
// ...
// Indicates to send { type: 'SOME_EVENT' } to the invoked service
actions: send({ type: 'SOME_EVENT' }, { to: 'some-service-id' })
entry: assign({
someActor: () => {
return spawn(someMachine, 'some-actor-name');
}
}),
// ...
// Send { type: 'SOME_EVENT' } to the actor ref
{
actions: send(
{ type: 'SOME_EVENT' },
{
to: (context) => context.someActor
}
)
};
다시 말하지만, send(...) 함수는 액션 생성자이며 이벤트를 명령적으로 보내지 않습니다.
대신 이벤트가 전송될 위치를 설명하는 액션 개체를 반환합니다.
console.log(send({ type: 'SOME_EVENT' }, { to: 'child' }));
// logs:
// {
// type: 'xstate.send',
// to: 'child',
// event: {
// type: 'SOME_EVENT'
// }
// }
자식 시스템에서 부모 시스템으로 보내려면 sendParent(event)를 사용하십시오.
(send(...)와 동일한 인수를 사용).
Raise action
raise() 액션 생성자는 상태 차트의 내부 이벤트 대기열에 이벤트를 넣습니다.
이는 이벤트가 인터프리터의 현재 "단계"에서 즉시 전송됨을 의미합니다.
(주 : 단일 단계에서 전환과 이벤트 해석을 배치처리하게 함.)
Argument | Type | Description |
event | string or event object | The event to raise. |
import { createMachine, actions } from 'xstate';
const { raise } = actions;
const raiseActionDemo = createMachine({
id: 'raisedmo',
initial: 'entry',
states: {
entry: {
on: {
STEP: {
target: 'middle'
},
RAISE: {
target: 'middle',
// immediately invoke the NEXT event for 'middle'
actions: raise('NEXT')
}
}
},
middle: {
on: {
NEXT: { target: 'last' }
}
},
last: {
on: {
RESET: { target: 'entry' }
}
}
}
});
Click on both STEP and RAISE events in the visualizer (opens new window)to see the difference.
Respond action (호출한 부모 서비스:머신 에 대한 응답 액션)
- origin - 이 이벤트의 수신자가 응답 이벤트를 원점으로 다시 send(...) 수 있도록 하는 문자열입니다.
Argument | Type | Description |
event | string, event object, or send expression | The event to send back to the sender |
options? | send options object | Options to pass into the send() event |
응답 액션을 사용하는 예
이것은 호출된 authServerMachine에 'CODE' 이벤트를 보내는 어떤 부모 서비스(authClientMachine)와
'TOKEN' 이벤트로 응답하는 authServerMachine을 보여줍니다.
const authServerMachine = createMachine({
initial: 'waitingForCode',
states: {
waitingForCode: {
on: {
CODE: {
// 요청을 보낸 머신에 응답하기.
actions: respond({ type: 'TOKEN' }, { delay: 10 })
}
}
}
}
});
const authClientMachine = createMachine({
initial: 'idle',
states: {
idle: {
on: {
AUTH: { target: 'authorizing' }
}
},
authorizing: {
invoke: {
id: 'auth-server',
src: authServerMachine
},
entry: send('CODE', { to: 'auth-server' }),
on: {
TOKEN: { target: 'authorized' }
}
},
authorized: {
type: 'final'
}
}
});
forwardTo action 4.7+
Argument | Type | Description |
target | string or function that returns service | The target service to send the most recent event to. |
사용 예시
import { createMachine, forwardTo, interpret } from 'xstate';
function alertService(_, receive) {
receive((event) => {
if (event.type === 'ALERT') {
alert(event.message);
}
});
}
const parentMachine = createMachine({
id: 'parent',
invoke: {
id: 'alerter',
src: () => alertService
},
on: {
ALERT: { actions: forwardTo('alerter') }
}
});
const parentService = interpret(parentMachine).start();
parentService.send({ type: 'ALERT', message: 'hello world' });
// => alerts "hello world"
Escalate action 4.7+
escalate() 액션 생성자는 오류를 상위 시스템으로 보내 오류를 에스컬레이션합니다.
이것은 상태 기계가 인식하는 특별한 오류 이벤트로 전송됩니다.
Argument | Type | Description |
errorData | any | The error data to escalate (send) to the parent |
import { createMachine, actions } from 'xstate';
const { escalate } = actions;
const childMachine = createMachine({
// ...
// This will be sent to the parent machine that invokes this child
entry: escalate({ message: 'This is some error' })
});
const parentMachine = createMachine({
// ...
invoke: {
src: childMachine,
onError: {
actions: (context, event) => {
console.log(event.data);
// {
// type: ...,
// data: {
// message: 'This is some error'
// }
// }
}
}
}
});
Log action
log() 액션 작성자는 현재 상태 컨텍스트 및/또는 이벤트와 관련된 모든 것을 기록하는 선언적 방법입니다.
두 개의 선택적 인수가 필요합니다.
Argument | Type | Description |
expr? | string or function | A plain string or a function that takes the context and event as arguments and returns a value to be logged |
label? | string | A string to label the logged message |
import { createMachine, actions } from 'xstate';
const { log } = actions;
const loggingMachine = createMachine({
id: 'logging',
context: { count: 42 },
initial: 'start',
states: {
start: {
entry: log('started!'),
on: {
FINISH: {
target: 'end',
actions: log(
(context, event) => `count: ${context.count}, event: ${event.type}`,
'Finish label'
)
}
}
},
end: {}
}
});
const endState = loggingMachine.transition('start', 'FINISH');
endState.actions;
// [
// {
// type: 'xstate.log',
// label: 'Finish label',
// expr: (context, event) => ...
// }
// ]
// The interpreter would log the action's evaluated expression
// based on the current state context and event.
Choose action
Argument | Type | Description |
conds | array |
주어진 조건이 true일 때 실행할 액션을 포함하는 객체 배열(아래 참조)
|
- actions - 실행할 액션 객체 조건?
- cond?- 해당 작업을 실행하기 위한 조건
경고
choose()를 사용하여
entry, exit 또는 actions를 통해 특정 상태/전환에서 실행되는 비조건부 액션으로 표시될 수 있는 액션을 실행하지 마십시오.
import { actions } from 'xstate';
const { choose, log } = actions;
const maybeDoThese = choose([
{
cond: 'cond1',
actions: [
// selected when "cond1" is true
log('cond1 chosen!')
]
},
{
cond: 'cond2',
actions: [
// selected when "cond1" is false and "cond2" is true
log((context, event) => {
/* ... */
}),
log('another action')
]
},
{
cond: (context, event) => {
// some condition
return false;
},
actions: [
// selected when "cond1" and "cond2" are false and the inline `cond` is true
(context, event) => {
// some other action
}
]
},
{
actions: [
log('fall-through action')
// selected when "cond1", "cond2", and "cond3" are false
]
}
]);
Argument | Type | Description |
getActions | function | A function that returns the action object(s) to be executed based on the given context and event (see below) |
Argument | Type | Description |
context | object | The current state context |
event | event object | The event object that triggered the actions |
Actions on self-transitions (자기 자신으로 상태 전환할 때 액션)
자기 전이(self-trantion)는 상태가 자기 자신으로 전환되는 경우이며, 이 상태에서 exit했다가 다시 entry할 수 있습니다.
자체 전환은 internal 또는 external 전환이 될 수 있습니다.
- internal 전환은 종료 및 재진입되지 않으므로 상태 노드의 진입 및 종료 작업은 다시 실행되지 않습니다.
- 내부 전환은 { internal: true }로 표시되거나 대상을 정의되지 않은 상태로 둡니다.
- 전환의 actions 속성에 정의된 작업이 실행됩니다.
- external 전환은 종료했다가 다시 들어가므로 상태 노드의 진입 및 종료 작업이 다시 실행됩니다.
- 모든 전환은 기본적으로 외부입니다. 명시적으로 { internal: false }로 표시할 수 있습니다.
- 전환의 actions 속성에 정의된 액션이 실행됩니다.
const counterMachine = createMachine({
id: 'counter',
initial: 'counting',
states: {
counting: {
entry: 'enterCounting',
exit: 'exitCounting',
on: {
// self-transitions
INC: { actions: 'increment' }, // internal (implicit)
DEC: { target: 'counting', actions: 'decrement' }, // external
DO_NOTHING: { internal: true, actions: 'logNothing' } // internal (explicit)
}
}
}
});
// External transition (exit + transition actions + entry)
const stateA = counterMachine.transition('counting', { type: 'DEC' });
stateA.actions;
// ['exitCounting', 'decrement', 'enterCounting']
// Internal transition (transition actions)
const stateB = counterMachine.transition('counting', { type: 'DO_NOTHING' });
stateB.actions;
// ['logNothing']
const stateC = counterMachine.transition('counting', { type: 'INC' });
stateB.actions;
// ['increment']
'FrontEnd' 카테고리의 다른 글
XState : 컨텍스트와 이벤트 모델링 (0) | 2022.05.05 |
---|---|
XState의 Context(양적 데이터) 알아보기 (0) | 2022.05.05 |
XState의 Actor 알아보기 (0) | 2022.05.04 |
XState 공식 문서 번역 : 서비스 호출(Invoking Services) (0) | 2022.05.01 |
XState와 비동기 2편: XState Actor와 함께 비동기를 안전하게 모델링 (0) | 2022.05.01 |