#Communicating between machines?

1 messages · Page 1 of 1 (latest)

low shale
#

Hi!

Suppose I have two persistent machines: a Canvas machine and a Menu machine. Canvas machine handles panning and zooming behavior. Menu machine handles menu visibility, positioning, option populating, etc. When the Menu machine is in a 'visible' state, I want to put the Canvas machine in a 'locked' state.

How should I set up communication between them? Curious what is best (or merely good) practice is.

There'll be only one service instance of each, so my initial naive thought is to simply use the Canvas service in the Menu machine and use it to send 'lock'/'unlock' events.

Thanks!

short gorge
#

Would it be crazy to handle it with parallel states? https://stately.ai/docs/xstate/states/parallel-states#targeting-multiple-parallel-states

You could have a root level event in a machine which swaps between the canvas locking and the menu opening, and vice versa

I tried doing this with the visualizer, but it doesn't work for some reason... Not sure of what your needs are but thought I'd throw the idea in to see if it's useful.

You could spawn the menu when its state is open, too — that seems like a tidy way to create and dispose of it as needed

A parallel state is a state separated into multiple regions of child states, where each region is active simultaneously.

icy snow
#

Yes, parallel states sounds like a good approach to me too

short gorge
#

@icy snow Should multiple targets work in the editor? For example:

createMachine({
  type: 'parallel',
  id: 'root',
  states: {
    canvas: {
      initial: 'unlocked',
      states: {
        unlocked: {
          initial: 'idle',
          states: {
            idle: {
              on: {
                POINTER_DOWN: 'panning',
              },
            },
            panning: {
              on: {
                POINTER_UP: 'idle',
              },
            },
          },
        },
        locked: {
          // No events here, can only be unlocked by root events
        },
      },
    },
    menu: {
      initial: 'closed',
      states: {
        open: {
          on: {
            SELECT_ITEM: {
              actions: ['selectItem', raise({type: 'CLOSE_MENU'})],
            },
          },
        },
        closed: {
          // No events here, can only be opened by root events
        },
      },
    },
  },
  on: {
    OPEN_MENU: {
      target: ['.canvas.locked', '.menu.open'],
    },
    CLOSE_MENU: {
      target: ['.canvas.unlocked', '.menu.closed'],
    },
  },
});

Importing this into the editor converts the multiple targets in OPEN_MENU and CLOSE_MENU to be a single target

icy snow
#

They are not supported yet

short gorge
#

Ah, phew. I thought I might be doing it wrong. I'm realizing I should be using this pattern more often. It's very powerful for mutually exclusive states

#

Those (in my experience) are actually fairly common in UIs, and really annoying to implement safely in a massive DOM full of willy nilly nodes loaded to the gills with event listeners and flag-based conditions

low shale
#

So raising events will let me send events to a parallel state from another parallel state, if I'm understanding it right

icy snow
low shale