4

I have a scenario where I have one parent machine and several child machines that can be spawned from the parent machine.

The current setup looks like this:

const parentMachine = Machine({
  context: {
    children: [] //can contain any number of child services
  },
  ...
  on: {
    ADD_CHILD: {
       actions: assign({
         children: (ctx, e) => {
           return [
             ...ctx.children,
             {
               ref: spawn(childMachine)
             },
           ];
         },
      }),
    },
    UPDATE_CHILDREN: {
      actions: ??? //need to somehow loop through children and send the UPDATE event to each service
    }
  }
});

When the parent machine receives the "UPDATE_CHILDREN" event, I want to update each of the child services. I know you can send batch events by passing an array to send, but I want each event to also be sent to a different service. I've only seen examples where they are sent to a single service at a time. I've tried several things, including the following:

UPDATE_CHILDREN: {
  actions: ctx => ctx.children.forEach(c => send("UPDATE", { to: () => c.ref }) //doesn't send 
}

Am I missing something obvious? Is this possible?

Drew Jex
  • 845
  • 2
  • 13
  • 24

2 Answers2

3

Ah, I bumped into exactly the same issue as you!

It turns out that if you give actions a function, it assumes the function to be the actual action, not a function that returns actions.

If you want to generate your actions based on context, you need to use a pure action:

import { actions } from 'xstate';

const { pure } = actions;

...

    actions: pure((context, event) =>
      context.myActors.map((myActor) =>
        send('SOME_EVENT', { to: myActor })
      )
    ),

This is a tricky mistake to fall into as you get no feedback that you're doing something wrong..

Acorn
  • 49,061
  • 27
  • 133
  • 172
2

Had a realization about how this is supposed to work in XState.

The references to the children are already being stored, so we can just basically send events to them directly without using the "to" property:

actions: ctx => ctx.children.forEach(c => c.ref.send("UPDATE"))
Drew Jex
  • 845
  • 2
  • 13
  • 24