Xstate: How to represent events that have multiple possible new states

Created on 12 Apr 2018  路  12Comments  路  Source: davidkpiano/xstate

What is the best way to represent an event having multiple possible new states
(based on some external state).

For example consider the following state machine:

{
  initialState: 'app',
  states: {
    'closed': {
      on: {
        'open': 'opened',       // for admins
        'open': 'doubleCheck',  // for non-admins
      }
    },
    'doubleCheck': {
      on: {
        'confirm': 'opened',
        'cancel': 'closed',
      }
    },
    'opened': {
      on: {
        'close': 'closed',
      }
    }
  }
}

When the user clicks open we want to move them to a different state based
on whether or not they are an admin (state which is not encoded in our state
machine).

Is there a best practice for handling this?

Would it just be to use cond for each transition? I guess this requires the
developer to ensure that both conditions will never evaluate to true at the same
time.

Apologies if this question has been answered before! I had a search through the docs and issues but couldn't find an answer.

documentation question

All 12 comments

Oh man, it looks like I've just found an example of this in the docs:

http://davidkpiano.github.io/xstate/docs/#/api/config (search for cond).

It looks like you would handle it like so:

{
  initialState: 'app',
  states: {
    'closed': {
      on: {
        'open': { 
          'opened': { cond: () => {} },
          'doubleCheck': {cond: () => {} }
        }      
      }
    },
    'doubleCheck': {
      on: {
        'confirm': 'opened',
        'cancel': 'closed',
      }
    },
    'opened': {
      on: {
        'close': 'closed',
      }
    }
  }
}

Although it isn't clear what would happen if both conditions evaluated to true. How does xstate know which state to move into?

Although it isn't clear what would happen if both conditions evaluated to true. How does xstate know which state to move into?

I have to add this to the docs, but they're evaluated in order. You can be more explicit about this by providing an array:

    'closed': {
      on: {
        'open': [
          { target: 'opened', cond: (extState) => { extState.user.type === 'admin' } },
          { target: 'doubleCheck', cond: () => {} },
          { target: 'someDefaultTarget' }
        ]
      }
    },

Which will evaluate each candidate transition in order, until it finds one where the cond is satisfied.

Ah, I saw the array form in an issue (https://github.com/davidkpiano/xstate/issues/43) but didn't know it was available to use. Good to know!

And thanks so much from the prompt reply!

I've been working on a converting some complex Redux Sagas into state machines and my design is moving ever closer to xstate by the day. I'm hoping I can just piggy back on your work with a small wrapper. Thanks for the great library, and for thinking through all these unusual edge cases!

Just a couple more questions if I may!

  • Would it be helpful if I added an example of this to the docs? If so should I just make a PR for this file (https://github.com/davidkpiano/xstate/blob/gh-pages/docs/guides/guards.md) or is there more to a docs change?

  • It looks like conditional transitions are not supported by the Statechart Visualizer (https://codepen.io/davidkpiano/full/ayWKJO/). Is there some way I can help out and add support for them?

One more question that I couldn't find an answer for in the docs:

  • What happens if an error is thrown in a cond function? Does it just cause an error to be thrown when calling machine.transition?

Would it be helpful if I added an example of this to the docs?

Yes! Just make it to gh-pages and you're good.

It looks like conditional transitions are not supported by the Statechart Visualizer

They'll be supported in the next version of the visualizer, which will be released as a separate package in a couple weeks.

What happens if an error is thrown in a cond function? Does it just cause an error to be thrown when calling machine.transition?

I'd think so. It'd be up to the interpreter on how to handle that error (whether you make the interpreter yourself or use a pre-made one, which will also be released in a couple weeks).

I've created a first PR which adds an interactive example of how to use conditional guards:

https://github.com/davidkpiano/xstate/pull/89

Awesome! I commented on it, let me know what you think.

Comments are great! I've updated the PR based on them.

I've had a moment to create a PR to update the docs with support for an array of state transition mappings:

https://github.com/davidkpiano/xstate/pull/92

Thanks so much @karl! Tiny comment but otherwise looks 馃憤

Closing this as docs have been updated to reflect what I've learnt 馃榾

Was this page helpful?
0 / 5 - 0 ratings

Related issues

ifokeev picture ifokeev  路  3Comments

bradwoods picture bradwoods  路  3Comments

mattiamanzati picture mattiamanzati  路  3Comments

kurtmilam picture kurtmilam  路  3Comments

carlbarrdahl picture carlbarrdahl  路  3Comments