XState > 親・子・兄弟Machine間での通信


親と子、親を通じて子から子へ伝える方法のメモ


https://xstate.js.org/docs/guides/communication.html#invoking-machines


https://codesandbox.io/s/parent-child-fetcing-nest-child-inter-forked-6o749?file=/src/parentMachine.js


# first

## 子マシン1号

```:childMachine1.js
import { createMachine, sendParent } from "xstate";

export const childMachine1 = createMachine({
  id: "remote",
  initial: "offline",
  states: {
    offline: {
      on: {
        WAKE: "online"
      }
    },
    online: {
      after: {
        1000: {
          actions: sendParent("CHILD1.COMPLETE")
        }
      }
    }
  }
});
```
## 子マシン2号

```:childMachine2.js
import { createMachine, sendParent } from "xstate";

export const childMachine2 = createMachine({
  id: "remote2",
  initial: "offline",
  states: {
    offline: {
      on: {
        WAKE: "online"
      }
    },
    online: {
      actions: sendParent("CHILD2.COMPLETE", {
        delay: 2000
      })
    }
  }
});
```


## 親マシン
entryで子マシンを２つ作ってContextに保持します。

```:parentMachine.js
import { Machine, assign, spawn, send } from "xstate";
import { childMachine1 } from "./childMachine1";

export const parentMachine = Machine({
  id: "parent-machine",
  initial: "idle",
  context: {
    child1: null,
    child2: null,
    url: null
  },
  states: {
    idle: {
      entry: assign({
        child1: () => spawn(childMachine1)
      }),
      on: {
        "running.start": { target: "child1_run" }
      }
    },
    child1_run: {
      after: {
        1000: {
          actions: send({ type: "WAKE" }, { to: (context) => context.child1 })
        }
      },
      on: {
        "CHILD1.COMPLETE": { target: "child1_completed" }
      }
    },
    child1_completed: {
      always: { target: "final" }
    },
    final: {
      type: "final"
    }
  }
});

```



# Messaging

## 親 → 子


参考：https://xstate.js.org/docs/guides/actors.html#quick-reference

### 親側

#### イベントを送信

```
{
  actions: send(
    { type: 'SOME_EVENT' },
    {
      to: (context) => context.someRef
    }
  );
}
```


#### データを含むイベントを送信

```
{
  actions: send(
    (context, event) => ({ ...event, type: 'SOME_EVENT' }), 
    {
      to: (context) => context.someRef
    }
  );
}
```


#### イベントと特定のデータを送信

```
{
  actions: send(
    { type: 'SOME_EVENT', data: someData },
    {
      to: (context) => context.someRef
    }
  );
}
```

### 子側

#### イベントを受信

SOME_EVENTで`event.data`を受信、fooへ遷移する。

```
fooState: {
  on: {
    SOME_EVENT: { target: foo },
  },
},
```


#### イベントと特定のデータを受信

SOME_EVENTで`event.data`を受信し、ContextのsomeDataへ保管した後、fooへ遷移する。

```
fooState: {
  on: {
    SOME_EVENT: {
      target: foo,
      actions: assign({
        someData: (context, event) => event.data,
      }),
    },
  },
},
```



## 子 → 親


### 子側

#### 親へイベントを送信

```
{
  actions: sendParent({ type: 'ANOTHER_EVENT' });
}
```


#### 親へイベントとデータを送信

```
{
  actions: sendParent((context, event) => ({
    ...context,
    type: 'ANOTHER_EVENT'
  }));
}
```


```
{
  actions: sendParent((context, event) => ({
    type: 'ANOTHER_EVENT', 
    {foo: data}
  }));
}
```





### 親側

#### イベントを受信

```
barState: {
...
  on: {
    COMPLETE: { target: "complete", }
  }
},
```


遅延

```
barState: {
...
  on: {
    COMPLETE: { target: "complete", { delay: 1000 }}
  }
},
```


#### へイベントとデータを受信


```
barState: {
  on: {
    COMPLETE: {
      actions: assign({
        todos: (_, event) => event.foo
      }),
      target: "complete"
    }
  }
},
```