Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Relative paths for In State Guards #349

Closed
hnordt opened this issue Feb 6, 2019 · 6 comments
Closed

Relative paths for In State Guards #349

hnordt opened this issue Feb 6, 2019 · 6 comments
Milestone

Comments

@hnordt
Copy link
Contributor

hnordt commented Feb 6, 2019

I'm working on a feature that will only accept TOGGLE if the child state success is active. For that I used an In State Guards and it worked pretty well.

      hidden: {
        initial: "loading",
        states: {
          loading: {
            invoke: {
              src: "loadSomething",
              onDone: "success",
              onError: "failure"
            }
          },
          success: {},
          failure: {}
        },
        on: {
          TOGGLE: {
            target: "visible",
            in: "hidden.success"
          }
        }
      },

I noticed that in: ".success" doesn't work, so I had to use in: "hidden.success", it's a "semi-absolute" path.

It would be great if in worked with relative paths, so target and in would be consistent:

          TOGGLE: {
            target: ".child",
            in: ".anotherChild"
          }
@davidkpiano
Copy link
Member

Closing this as it doesn't make sense to support it. For this example, you can simply specify the transition on the success state:

success: {
  on: { TOGGLE: '#visible' } // or whatever the ID is
}

Or you can use final states to accomplish a similar thing. The difference is that this says:

In the "success" state, when the TOGGLE event occurs, transition to "visible"

Rather than:

In the "hidden" state, when the TOGGLE event occurs, transition to "visible" but only if in the "success" child state.

The first one is simpler and should be preferred.

@hnordt
Copy link
Contributor Author

hnordt commented Sep 25, 2019

My example was just a simplification of the real world use case. I just wanted to highlight the relative in guards feature request.

@Andarist
Copy link
Member

I agree that this should be supported - the handling of relative paths should be consistent, even if In() usage is discouraged in XState.

@davidkpiano davidkpiano reopened this Sep 25, 2019
@Andarist
Copy link
Member

One more thing I've discovered while looking into this - seems like in handles relative~ patterns, but this works completely different from targets. The matching happens on grandparent state and there are explicit tests covering this:
https://github.com/davidkpiano/xstate/blob/10d873df90781646db82fb83694de31ff2ff529c/test/stateIn.test.ts#L170

This makes it unobvious what the .child pattern should do right now for in - making it work like target would possibly makes things even more inconsistent? Ain't sure.

What's the reasoning behind this grandparent matching there @davidkpiano ? Either way - I believe we shouldn't touch this in v4 and just clean up both targets and in in v5 to make them act consistently.

@davidkpiano
Copy link
Member

What's the reasoning behind this grandparent matching there @davidkpiano

Good question. This goes back to the original Statecharts paper:

Screen Shot 2019-09-25 at 8 09 05 PM

Screen Shot 2019-09-25 at 8 09 18 PM

The original purpose for the in ... state guards was to match against orthogonal child states; that is, state nodes on the same level as a given state node. To identify that kind of state node relatively and unambiguously, you need two things:

  1. the parent key
  2. the child key

Therefore, matching in: 'some.state' was relative to the grandparent to meet those criteria.

When using in for this purpose, it's somewhat intuitive (do this transition if the machine is in this "cousin" state), but when not, I agree, it's highly unintuitive.

@Andarist
Copy link
Member

The original purpose for the in ... state guards was to match against orthogonal child states; that is, state nodes on the same level as a given state node.

Is "the same level as a given state node" stated somewhere or is it just an interpretation? Just curious. The use case itself sounds perfectly reasonable - that's also what is stated in SCXML:

This predicate allows coordination among parallel regions.
Seems to me though (at least at first) that this coordination shouldn't be limited to "same level" nodes - like parallel state can itself be a compound one and the same means of coordination should be possible.

In the current implementation it also only checks unconditionally grandparent - without taking into account that it might not be a parallel state. So this can bring even more confusion for people.

Just some random thoughts after reading through this 😉

I still believe we should go through all possible use cases, combinations etc and design targets & in in a cohesive manner - but that has to wait for v5 (gonna add this issue to v5 milestone if you don't mind)

@Andarist Andarist added this to the 5.0 milestone Sep 26, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants