Xstate: Array conditions

Created on 7 Apr 2019  路  9Comments  路  Source: davidkpiano/xstate

Bug or feature request?

Feature request

Description:

Would be neat if cond could accept array of guards, without this I have to make multiple transient states when my logic is more complex.

(Feature) Potential implementation:

It's not a breaking change and the API would be something like this:

'': [
    { target: 'foo', cond: ['isOnline', 'hasForm'] }
]
enhancement

Most helpful comment

Good point. I'll implement it - it's on the roadmap.

All 9 comments

This seems like it would work well with custom guards 馃摌 - the tricky thing with "just allowing arrays" is that it's ambiguous whether it means isOnline && hasForm or isOnline || hasForm. But you can easily do this:

const every = (...guards) => ({
  type: 'every',
  guards
});

const guards = {
  isOnline: ctx => ctx.status === 'online',
  hasForm: ctx => ctx.user.hasForm === true
};

// ...
'': [
  {
    target: 'foo',
    cond: every('isOnline', 'hasForm')
    // => { type: 'every', guards: ['isOnline', 'hasForm'] }
  }
]

// ...
// machine options
{
  guards: {
    ...guards,
    every: (ctx, event, { guard }) => {
      const { guards } = guard;
      return guards.every(guardKey => guards[guardKey](ctx, event));
    }
  }
}

What do you think about that for a solution? Much more flexible than having XState be opinionated (and potentially too magical/black-box like) about custom guards.

I don't have much problem with the proposed solution, I've tackled the problem at hand in a similar fashion at the moment. My reasoning behind requesting this was that basically OR might be already represented by:

'': [ { target: 'foo', guard: 'a' }, { target: 'foo', guard: 'b' } ]

and that custom guards are harder to serialize.

I'll consider it. Good to look at prior art too: https://states-language.net/spec.html#choice-state

This feature would be really nice. An array of two strings (e.g. cond: ['isOnline', 'hasForm']) would be much easier to read and write than custom guards are.

I think an array would work as an assumed && since the || alternative can already be implemented using an array of transitions (as @Andarist mentioned) and the pattern already exists with the actions arrays (i.e. every function in the array is fired).

馃

Good point. I'll implement it - it's on the roadmap.

Good point. I'll implement it - it's on the roadmap.

Thank you!

Good point. I'll implement it - it's on the roadmap.

Any chance this would also show up for the choose action?
choose([{ cond: ['cond1', 'cond2'] }, ... ])

@cybervaldez That will most likely look like:

choose([
  { cond: and('cond1', 'cond2') }
])

Or something similar.

But you can easily do this

If anyone stumbles across this workaround, I had to make a few changes to the every guard to make it work:

- every: (ctx, event, { guard }) => {
-   const { guards } = guard;
-   return guards.every(guardKey => guards[guardKey](ctx, event));
  }
+ every: (ctx, event, { cond }) => {
+   const { keys } = cond;
+   return keys.every(guardKey => guards[guardKey](ctx, event));
  }
Was this page helpful?
0 / 5 - 0 ratings

Related issues

3plusalpha picture 3plusalpha  路  3Comments

laurentpierson picture laurentpierson  路  3Comments

drmikecrowe picture drmikecrowe  路  3Comments

ifokeev picture ifokeev  路  3Comments

greggman picture greggman  路  3Comments