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

Bug: State entry action's actor value mismatch #4908

Open
carlos-sonatafy opened this issue May 23, 2024 · 9 comments
Open

Bug: State entry action's actor value mismatch #4908

carlos-sonatafy opened this issue May 23, 2024 · 9 comments

Comments

@carlos-sonatafy
Copy link

XState version

XState version 5

Description

While trying to read the actor's current value/active state using self.getSnapshot().value, the actual value is that of the actor before entering the new state.

As an example, consider the following statechart:

https://stately.ai/registry/editor/embed/0e3cf46a-a74e-4fc2-8161-dcbcec137965?machineId=522c8104-73e6-40d6-922c-30917b52733f

const myAction = assign(({ self }) => {
  console.log(self.getSnapshot().value);

  return {};
})

The action prints First State instead of Second State.

Expected result

Because the action is an entry action for a specific state - not a transition - I'd expect the actual value to be Second State

Actual result

The value is First State

Reproduction

https://stately.ai/registry/editor/0e3cf46a-a74e-4fc2-8161-dcbcec137965?machineId=522c8104-73e6-40d6-922c-30917b52733f

Additional context

No response

@davidkpiano
Copy link
Member

This is working as expected. The snapshot isn't committed until all of the actions have been executed.

@carlos-sonatafy
Copy link
Author

carlos-sonatafy commented May 24, 2024

@davidkpiano I mean, I understand that portion - and I know my knowledge about statecharts is lesser than yours - but it kind of doesn't make sense.

If we're talking about an action that executes while entering a state, shouldn't said action be able to consult the statechart's active state?

And if that holds true, shouldn't the active state (value) be the same state it was entered and not the one before transitioning?

@davidkpiano
Copy link
Member

@crls-dray Would you want something like this?

// PSEUDOCODE
const myAction = assign(({ stateNode }) => {
  console.log(stateNode.id); // e.g. "someMachine.loading"

  return {};
})

@carlos-sonatafy
Copy link
Author

carlos-sonatafy commented May 28, 2024

Yes, that might work - not sure how'd that translate for parallel states, that's why I thought the actual value would be the same as the snapshot.value. I'm not using parallel states, just mentioning it.


My use-case is that I have an action which is loading some data into the context, and such data is active-state specific.

The statechart is being defined in Stately Studio, the main purpose is to allow non-dev coworkers of mine to model the statechart with minimal overhead due to implementation specifics - thus why I can take away the implementation specifics if I'm able to load the info based on the current/active state.

@carlosbensant
Copy link

I have the same use case as @cris-dray. I have a state property in my context loadingStatus.text that I want to be updated every time a state transition occurs.

@carlosbensant
Copy link

carlosbensant commented May 28, 2024

Based on vck3000's response, I can see this has been the current implementation for a long while, I would like to understand but it doesn't make sense as for me, anytime you add an entry action property to a state, we expect it to match the current state value where the action is placed.

@carlosbensant
Copy link

My current implementation looks like this but I have plenty of actions and state transitions (to notify the user, to update timestamp, etc):

import { setup, assign } from 'xstate';

const createDogMachine = (contextFromDB) => {
  return setup({
    actions: {
      updateContext: assign({
        state: (_, params) => params.state,
      }),
    }
  }).createMachine({
    id: 'dog',
    initial: contextFromDB.state || 'asleep',
    context: contextFromDB,
    states: {
      asleep: {
        entry: [
          {
            type: 'updateContext',
            params: {
              state: 'asleep', // current state
            }
          }
        ],
        on: {
          'wakes up': 'awake',
        }
      },
      awake: {
        entry: [
          {
            type: 'updateContext',
            params: {
              state: 'awake', // current state
            }
          }
        ],
        on: {
          'falls asleep': 'asleep',
        }
      },
      //...
    }
  });
}

gist: https://gist.github.com/carlosbensant/21adcd22373d16f1388f283a9b53a17f

@davidkpiano
Copy link
Member

cc. @Andarist - I know that you had opinions/thoughts on this

@davidkpiano davidkpiano reopened this May 31, 2024
@Andarist
Copy link
Member

I think giving the user access to the state node that triggers the action would be nice, something like in #4217

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

4 participants