Xstate: Forbidden transitions not recognized

Created on 31 Jan 2020  路  7Comments  路  Source: davidkpiano/xstate

Have noticed that forbidden transitions in strict mode are not accepted. The machine throws a strict mode error.

As per documentation transitions defined as undefined or {actions: []} are meant to be recognized as valid transitions. When a matching event is sent the machine throws an error.

Example:

import { Machine } from 'xstate';


const testMachine = Machine({
  strict: true,
  initial: 'someState',
  states: {
    someState: {
      on: {
        FORBIDDEN_UNDEFINED: undefined,
        FORBIDDEN_EMPTY_ACTIONS: { actions: []},
        FORBIDDEN_OK: 'someState',
      }
    },
  }
})

let currentState = testMachine.initialState

function send(event) {
  currentState = testMachine.transition(currentState, event)
  console.log(currentState.value)
}

send('FORBIDDEN_OK') // someState 
send('FORBIDDEN_UNDEFINED') // Machine '(machine)' does not accept event 'FORBIDDEN_UNDEFINED'
send('FORBIDDEN_EMPTY_ACTIONS') // Machine '(machine)' does not accept event 'FORBIDDEN_EMPTY_ACTIONS'

Have been broken since 4.0.0-11

bug has workaround

All 7 comments

Thanks for the report - I will take a look today at this, should be an easy fix. That said - could you describe your use case for strict mode? We are considering removing it in the future.

Thanks for the prompt reply.
I don't intend to retain the strict mode in production, just for testing. In my case, a third party library emits a sequence of events, some undocumented. So I would like to catch all & fail-fast if some events are become unaccounted for in the machine. So outside testing I don't have a valid case for the strict mode.
Hope this helps
Irek

I've identified the problem, I'm just unsure if we should fix this right now. I need to think about it more and discuss it with David.

When we drop strict mode from the core in the next major, our recommendation for a replacement will be just using a wildcard descriptor on the root - you could try this strategy already.

Machine({
  on: {
    '*': { /* your logic of failing */ }
  }
})

Thanks for the tip, works fine and seems to do exactly the same as the strict mode.

Ok, the actual workaround to simulate a forbidden transition is to simply specify the self-transition explicitly, i.e. 'SOME_EVENT': 'sameState', and that's fine.

If you refer to using wildcard event transition instead of strict mode it has produced some unexpected behaviour.

The wildcard action '*': { /* your logic of failing */ } placed at the root level appears to get triggered by an event for which there is, in fact, a state transition with false condition. Additionally, self-transitions that are internal by default become external self-transitions that trigger service on re-entry. I didn't dig into it any further but, perhaps, there is more to it. Is this something that can be explained?

Machine({
  ...
  on: {
      '*': { /* logic of failing */ }
  },
  someState: {
      invoke: {
        id: 'serviceId',
        src: 'someService'
      },
      on: {
          EVENT_FALSE: { actions: 'someAction', cond: false }, 
          EVENT_EXTERNAL_TRUE: { actions: 'someAction' },
          EVENT_INTERNAL_TRUE: { actions: 'someAction', internal: true }
    }
  }
})
...
send('EVENT_FALSE') // triggers logic of failing
send('EVENT_EXTERNAL_TRUE') // doesn't trigger logic of failing, however, triggers service on self-transition?
send('EVENT_INTERNAL_TRUE') // doesn't trigger any of the above

This is expected. The guarded transition for EVENT_FALSE needs some condition (please use cond: () => false) to be satisfied. If it is not, then no transition matches, and it's as if there is no transition for that "falsy" event, so it falls back to '*'.

However, you can be explicit:

EVENT_FALSE: [
  { actions: 'someAction', cond: () => false },
  { target: undefined }
]

Thanks for the explanation!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

hnordt picture hnordt  路  3Comments

bradwoods picture bradwoods  路  3Comments

3plusalpha picture 3plusalpha  路  3Comments

jfun picture jfun  路  3Comments

laurentpierson picture laurentpierson  路  3Comments