[xstate] machine간에 부모 - 자식 관계 (트리)로 state, event를 관리하는 방법
문제
모델을 xstate에 떨어뜨려 가면, 아무래도 machine을 분할해, 1개의 machine가 다른 machine를 관리해 전체로서의 state를 관리하고 싶은 것이 나온다. (예: one-to-many, many-to-many)
해결 방법
부모 machine에 아이 machine을 갖게 한다
방법은
뿐이라고 생각한다.
context에 유지
interface Context{
child: ActorRefFrom<typeof childMachine>
}
...
{
states: {
idle: {
entry: ["setChild"] // どこでもどのタイミングでも問題ない
}
}
},
{
actions: {
setChild: assign((context, event) => ({...context, child: spawn(childMachine)}))
}
})
포인트는
spawn
를 사용해, context에 두어 두는 것. context에 두기 때문에, context를 갱신할 때까지 계속 존재한다.주의점으로서는 assign할 때에
assign((context, event) => ({ ...context, child: spawn(...) }))
assign({
child: (context, event) => spawn(...)
})
그렇지 않으면 정상 작동하지 않습니다.
assign({
child: spawn(...) // ❌
})
이렇게 하면 에러도 아무것도 나오지 않고, 움직이지 않는다.
기계를 invoke
{
states: {
idle: {
invoke: {
id: "childMachineId",
src: childMachine,
onDone: { ... }
}
}
}
}
invoke이므로, invoke된 state내에서만 존재하고 있다. 용도로는 통상의 invoke service로 해서는 복잡하고, machine에 떨어뜨려 버리는 편이 간결하게 정리되는 때에 편리.
events
부모로부터 아이에게 이벤트를 던지는 법
send를 사용한다. send는 일반적인 invoke service를 향해 event를 던지기도 하지만, 부모와 자식 관계가 있는 machine이면 부모로부터 아이에게 event를 던질 수도 있다.
event를 던질 때에는 아이가 context내에 들어 있다면 그 객체(또는 spawn시의 이름), invoke된 machine이면 그 ID를 지정해 줄 필요가 있다.
주의점으로서 action 안에서. 반드시
context.child.send(...)
의 send를 통해 이벤트를 보내야합니다.context 내의 spawn된 machine의 경우
import { actions, ... } from "xstate" // actions.pureを使う場合
{
on: {
"SOME.EVENT": {
actions: [send("EVENT.TO.CHILD", { to: (context, event) => context.child })]
// もしくは
// actions: [send("EVENT.TO.CHILD", { to: ”spawned-child” })]
// またはこうやっても良い
// actions: ["sendToChild"]
}
}
},
{
actions: {
setChild: assign((context, event) => ({
...context,
child: spawn(childMachine, { name: "spawned-child" })
// nameは指定できるが、contextからオブジェクト自体を参照できるのでしなくても問題ない
})),
sendToChild: actions.pure((context, event) => {
// context, eventによって振り分けが必要だったり、
// コードが煩雑になる場合はこういう風にactionに落とし込んだほうがスッキリする
return send("EVENT.TO.CHILD", { to: context.child })
})
}
}
invoke된 machine의 경우
{
on: {
"SOME.EVENT": {
// ここではidを直接指定する以外ない
actions: [send("EVENT.TO.CHILD", { to: "childMachineId" })]
}
},
states: {
idle: {
invoke: {
id: "childMachineId",
src: childMachine,
onDone: { ... }
}
}
}
}
복수의 아이 machine에 send 하고 싶은 경우
import { send } from "xstate"
에 쓰려고 하면 쓸 수 없지만 코드가 번잡하기 때문에 actions: [...]
를 사용한다.각 child에 대해 send를 생성하고 그것을 배열로 반환합니다.
import { actions, ... } from "xstate"
{
context: {
childMachines: []
},
on: {
"ADD.CHILD": {
actions: ["addChild"]
}
"EVENT.TO.CHILD_MACHINES": {
actions: ["sendToChildren"]
}
},
},
{
actions: {
sendToChildren: actions.pure((context, event) => {
return context.childMachines.map((child) => send("EVENT.TO.CHILD", { to: child }))
})
}
}
아이로부터 부모에게 event를 던질 경우
import { sendParent, ... } from "xstate"
{
on: {
"SOME.EVENT": {
actions: [sendParent("EVENT.TO.PARENT")]
}
}
}
spawn의 sync와 autoForward
sync
부모로부터 아이를 spawn 할 때, 아이의 state 가 변경할 때마다 부모에게 통지를 하는지 어떤지. 아이의 상태가 A에서 B로 바뀌면 부모
actions.pure
가 반응합니다. (아마 parentService.onTransition((state) => ...)
음)이것이라고 반응이 너무 많다고 하는 경우는, sync는 사용하지 않고 sendUpdate를 아이의 요소요소로 사용하면, 정확하게 부모에게 통지를 할 수 있다.
아이
{
...
states: {
idle: {},
stateA: {
entry: [sendUpdate()]
},
}
}
autoForward
는 부모로 받은 event를 그대로 아이에게도 흘릴지 어떨지. click 이벤트의 이벤트 버블링적인.
부모와 자식 관계가 좀처럼 꿀이 아닌 한은 사용하지 않는 것.
참고
invoke한 machine에서의 부모와 자식 관계 htps : // 기주 b. 코 m / m r r / ゔ ぃ에서 어쨌든 s / t ree / 마이 / src / 마을 s 에 machine이 있습니다.
매우 간단하게 만든 샘플.
Reference
이 문제에 관하여([xstate] machine간에 부모 - 자식 관계 (트리)로 state, event를 관리하는 방법), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다
https://qiita.com/github0013@github/items/a7a2ea525f59f1e07e3e
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념
(Collection and Share based on the CC Protocol.)
Reference
이 문제에 관하여([xstate] machine간에 부모 - 자식 관계 (트리)로 state, event를 관리하는 방법), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://qiita.com/github0013@github/items/a7a2ea525f59f1e07e3e텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)