#setup two state machines to talk to eachother

1 messages · Page 1 of 1 (latest)

opaque fjord
#

I tried the most minimal example I could think of. A parent machine that sends a message to a child state machine, upon entering its only state. The child state machine simply acknowledges receipt of the message in the log.

But I keep getting the error message in the log: " Unable to send event to actor 'child' from machine 'main'."

What am I missing?

import './App.css';
import { setup, log, sendTo } from 'xstate';
import { useMachine } from '@xstate/react';

const childMachine = setup({
  types: {
    events: {} as { type: 'foo' },
  },
}).createMachine({
  id: 'child',
  on: {
    foo: { actions: log('CHILD GOT FOO') },
  },
});

const mainMachine = setup({
  actions: {
    pingChild: sendTo('child', { type: 'foo' }),
  },
  actors: {
    child: childMachine,
  },
}).createMachine({
  id: 'main',
  initial: 'running',
  states: {
    running: {
      entry: [log('ENTERED PARENT RUNNING'), 'pingChild'],
    },
  },
});

function App() {
  const [_] = useMachine(mainMachine);
  return (
    <h1>Hello</h1>
  );
}

export default App;
vague fractal
#

You need to invoke (or spawn) the child actor so it actually runs and can receive events.

Assuming your child machine should have the same lifecycle as your mainMachine (i.e. start and stops at the same time), you can add invoke on your main machine:

invoke: {
  id: 'child', // Id for `sendTo()`
  src: 'child', // Name of the actor to invoke
}

State machines can “invoke” one or many actors within a given state. The invoked actor will start when the state is entered, and stop when the state is exited. Any XState actor can be invoked, including simple Promise-based actors, or even complex machine-based actors.

opaque fjord
#

thanks @vague fractal that did the trick!

opaque fjord
#

@vague fractal So the child machine works, but now I realize, I don't know how to access the child machine from outside of the parent machine.

I realize I could probably "mirror" the relevant parts of a child's context in the parent context, but it'd be cleaner if I could just somehow get the child's actor from the parent actor (for use with e. g. useSelector).. Is that possible somehow?

vague fractal
#

snapshot.children gives you access to all invoked child actors.

The first selector gives you access to the actor ref, the 2nd then subscribes to it.
I don't know if there is an easier way to do this 🤔

const actor = useActorRef(mainMachine);
const childRef = useSelector(actor, (x) => x.children.child);
const snapshot = useSelector(childRef, (x) => x.context.foo);