Xstate: Bug: send action in transient state; Maximum call size stack exceeded

Created on 28 Mar 2019  路  2Comments  路  Source: davidkpiano/xstate

Bug or feature request?

Bug

Description:

When firing a send action in a transient state, a RangeError: Maximum call size stack exceeded is encountered.

Link to reproduction or proof-of-concept:

https://codesandbox.io/s/n7z61nqw4?fontsize=14

invalid question

Most helpful comment

This is working as expected, and indicates some faulty logic in the statechart design. Basically, this logic:

    decide: {
      on: {
        "": [
          {
            cond: ctx => ctx.value < 0.5,
            actions: send("LOWER")
          },
          {
            cond: ctx => ctx.value >= 0.5,
            actions: send("HIGHER")
          }
        ],

says, in plain English:

When you transition to the decide state, transition back to the decide state. Also, always send the LOWER or AFTER event after transitioning depending on if the value is higher or lower.

That's infinitely recursive 馃槰

Instead, consider modeling this as having an idle state (which you already have) which is a "resting" state for after you've already decided:

https://codesandbox.io/s/l29y6zmo77

All 2 comments

This is working as expected, and indicates some faulty logic in the statechart design. Basically, this logic:

    decide: {
      on: {
        "": [
          {
            cond: ctx => ctx.value < 0.5,
            actions: send("LOWER")
          },
          {
            cond: ctx => ctx.value >= 0.5,
            actions: send("HIGHER")
          }
        ],

says, in plain English:

When you transition to the decide state, transition back to the decide state. Also, always send the LOWER or AFTER event after transitioning depending on if the value is higher or lower.

That's infinitely recursive 馃槰

Instead, consider modeling this as having an idle state (which you already have) which is a "resting" state for after you've already decided:

https://codesandbox.io/s/l29y6zmo77

Got it. Transient states require a target.

We'll make sure to head over to another state before firing off a send like that.

Using send in the way we do stems from the need to target a state without IDs. We use a single statechart to describe an entire app, and use mixins to separate sections (we did manage a structure where each section was a separate invoked Machine instance, but passing data back and forth became very cumbersome. Clean, but cumbersome. A single context is a must for us).

However, re-using these "sections", i.e. child states, proves problematic as the same child states could be mixed in more than once. For example, imagine a "modal" statechart that is mixed in twice in the main "app" statechart, but in different places.

We end up leveraging send to communicate back to the parent state, and letting it decide where to go next.

e.g.

const acceptConfig = {
  initial: 'display',
  states: {
    'display': {
      on: {
        CLICK_ACCEPT: 'accepted'
      }
    },
    'accepted': {
      type: 'final',
      onEntry: send('ACCEPTED')
    }
  }
}

const appConfig = {
  initial: 'app',
  states: {
    'app': {
      initial: 'intro'
      states: {
        'intro': {
          on: {
            NEXT: 'terms'
          }
        },
        'terms': {
          on: {
            BACK: 'intro',
            ACCEPTED: 'eula'
          },
          ...acceptConfig
        },
        'eula': {
          on: {
            BACK: 'terms',
            ACCEPTED: 'home'
          },
          ...acceptConfig
        },
        'home': {
        }
      }
    }
  }
}

The bubbling mechanics of send are perfect here. I just wish the same mechanics applied to target, i.e. look for a sibling state, if not found, go up a level, look for a sibling state, if not found, go up a level, and so on. It seems the direction is more "id" based though, based on this conversation (https://github.com/davidkpiano/xstate/issues/52).

EDIT: in re-reading my example, bubbling a state change isn't actually better than using send. I'm not sure what the cleaner way of doing this is, where you aren't using actions for this scenario... the solution may deviate from the SCXML too much, I don't know. Anyway, just thought I'd share this use case with you, as it underpins many of our apps now.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

jfun picture jfun  路  3Comments

dakom picture dakom  路  3Comments

mattiamanzati picture mattiamanzati  路  3Comments

drmikecrowe picture drmikecrowe  路  3Comments

laurentpierson picture laurentpierson  路  3Comments