xstate: onTransition does not fire after every transition

Description onTransition does not fire with `always’

Expected Result A few choices:

  • document that onTransition only fires when the “machine ‘settles’” & keep behavior as-is
  • onTransition always fires, including with always
  • configure if onTransition fires with always, or make onTransitionAlways

Reproduction https://tombyrer.github.io/cat-christmas/cat-christmas

Additional context reference issue

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Reactions: 4
  • Comments: 16 (13 by maintainers)

Most upvoted comments

I firmly believe that the issue presented here is sound. From my PoV, it’s not about functional shortcomings (I don’t have a use case that is not satisfied by the current APIs) but rather about semantic discrepancies.

If we call something a transition and we later provide the onTransition callback I would fully expect for it to be fired after every transition. Otherwise, we just end up confusing new learners because it’s not obvious that this actually behaves differently. And as shown by the issue here - it’s not a theoretical learning problem because @tomByrer (and myself in the past as well) has assumed a different behavior than the one that is currently implemented. This becomes even more of a problem because we’ve recommended using the onTransition to handle some logic whenever something happens (like getting a particular value in the context) in multiple issues - and currently some occurrences of the desired situation might be “skipped”, which is not obvious.

I understand that this mainly~ comes from the fact that this is called after machine.transition() which actually resolves all microsteps but maybe then this method should not be called transition either? This is a low-level API and as such, I actually wouldn’t mind a different resolution either - we could rethink if this actually should resolve all microsteps. Maybe it should be up to the interpreter to handle the resolution of the microsteps? From the final consumer’s PoV this actually wouldn’t matter much (unless they depend on the raw Machine API - but those users are already “advanced” so I don’t mind requiring more of them). This could actually simplify various stuff like SCXML compatibility with action order & error handling, semantic correctness, and even visualization (I believe that representing microsteps on the visualization is also important for the reasoning process, at times it might not be obvious why we have jumped directly from A to B without seeing the whole “path”)

Otherwise, we just end up confusing new learners because it’s not obvious that this actually behaves differently.

Yet a new learner here +1 come from XState Learning Curve

Looking forward to using the onMicrostep in v5!

And just my 2 cents: I feel it’s better to make a breaking change in v5: (from a new learner perspective)

  1. change onTransition in v4 to onBatchTransition in v5
  2. onTransition in v5 should be the new onMicrostep

This will be coming to v5:

// TENTATIVE API

service.onMicrostep(state => {
  // intermediate state
  console.log(state);
});

Where can I find document about the APIs for inspection for nodejs program?

Writing these docs this weekend.

See the latest merged PR for the inspect API to see usage.

While I do lean more towards Andarist’s perspective of “confusing new learners” (like me 😃 ). I want to provide counter perspective; skipping always {} does provide a neat way to separate out each ‘thing to do’ into individual steps, rather than having to combine an action step & timer step like I did with play{} state

In my demo above, I assumed that all always{} were triggered in onTransition, where I was going to update the DOM.

I don’t have a strong use-case for onTransitionAlways right now; just brainstorming. There are workarounds, I don’t need to have `it…

Better docs of how the current system works would help if you don’t want to add ; I was not aware of ‘microsteps’ in XState until yesterday, despite referring to the docs & Discussions 100 times.