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

onEnter does not execute if state is initialState #2

Closed
smiLLe opened this issue Dec 8, 2020 · 12 comments
Closed

onEnter does not execute if state is initialState #2

smiLLe opened this issue Dec 8, 2020 · 12 comments

Comments

@smiLLe
Copy link

smiLLe commented Dec 8, 2020

Hi, i am doing some tests using your great lib :)

Unfortunately i am running into a problem where onEnter does not execute

test('foo', () async {
    int i = 0;
    final machine = StateMachine.create((g) => g
      ..initialState<StateA>()
      ..state<StateA>((b) => b..onEnter((_, __) async => i++)));
    await machine.waitUntilQuiescent;
    expect(i, equals(1));
  });

onEnter will never execute if you are using .initialState<>. This does also not work for nested trees.
I am using version 0.10.1

@bsutton
Copy link
Collaborator

bsutton commented Dec 10, 2020

Let me have a look. I will set up a unit test.

I've just released an update and setting page breaks on states now works nicely.

bsutton added a commit that referenced this issue Dec 10, 2020
@bsutton
Copy link
Collaborator

bsutton commented Dec 10, 2020

I've just released 0.13 which has a fix for it.

I'm also interested in any details you care to share about how you are using the library.

@smiLLe
Copy link
Author

smiLLe commented Dec 10, 2020

Thanks for the update, i will give it a try later today.

As for my use case i was trying to implement something like this.

  MyContext context;
  StateMachine.create((g) => g
    ..initialState<StateA>()
    ..state<StateA>((b) => b
      ..onEnter((_, __) async {
        final data = await loadDataA();
        context = context.udpdate(data);
        machine.applyEvent(GoNext);
      })
      ..on<GoNext, StateB>()
      )
    ..state<StateB>((b) {}));

@smiLLe
Copy link
Author

smiLLe commented Dec 10, 2020

Also what i don't understand is why the transition/onEnter has to be async? Looking at other statemachines (xstate for example) the transition is always sync.

@bsutton
Copy link
Collaborator

bsutton commented Dec 10, 2020 via email

@smiLLe
Copy link
Author

smiLLe commented Dec 15, 2020

Is there a way to test against a parallel, nested machine?
something like

if (machine.inState([Login, User, IsValid]) && machine.inState([Login, Email, IsValid])) {
  return FooWidget();
}

I am using the IsValid Type twice but in different parallel states

@bsutton
Copy link
Collaborator

bsutton commented Dec 15, 2020

Firstly the FSM should throw an error if you try to use the same state twice. If it doesn't now it will shortly:)

We could add a method like the above suggestion but I'm concerned you are trying to solve the problem in the wrong way.

Instead you should consider collapsing the coregions down to a single state with an onjoin.

Note I found problems with onjoin yesterday for which I've pushed a fix but I still need to fully test it which is today's job.

@bsutton
Copy link
Collaborator

bsutton commented Dec 15, 2020

Can you provide some more detail around what you are trying to do.

One of the issues I'm grappling with is what api's we need to make fsm2 integrate nicely with flutter.

Having to make calls to 'isInState' feels like the fsm2 api isn't doing the right thing.

If you can provide a worked example maybe we can come up with a better solution.

@bsutton
Copy link
Collaborator

bsutton commented Dec 16, 2020

I've just published 0.15.0 which has fixes for onJoin.

@smiLLe
Copy link
Author

smiLLe commented Dec 16, 2020

I had no particular use case. I am just trying to see how this statemachine works :)
I am currently trying to implement the following machine:

  • StateA (onEnter -> send Transition to StateB)
  • StateB
    • StateC
class StateA implements State {}

class FromAToB implements Event {}

class StateB implements State {}

class StateC implements State {}

test('foo', () async {
    int i = 0;
    StateMachine machine;
    machine = StateMachine.create((g) => g
      ..initialState<StateA>()
      ..state<StateA>((b) => b
        ..onEnter((_, __) async {
          await Future.delayed(Duration(microseconds: 10));
          machine.applyEvent(FromAToB());
        })
        ..on<FromAToB, StateB>())
      ..state<StateB>((b) => b
        ..initialState<StateC>()
        ..state<StateC>((b) => b
          ..onEnter((_, __) async {
            i = 1;
          }))));
    await machine.waitUntilQuiescent;
    expect(i, equals(1));
  });

This would be an actual use case for me, where i have to request some async data in StateA and then i want to transition to StateB where StateB is nested. StateB will set its child state to StateC.
But that is not going to happen here.
I am using version 0.15.0

@bsutton
Copy link
Collaborator

bsutton commented Dec 16, 2020

Sorry my question was more around how you intend to wire it into flutter?

@smiLLe
Copy link
Author

smiLLe commented Dec 16, 2020

Oh i think i would use riverpod to create and share the statemachine and its state.
In my Widgets i would then use something like this:

final state = useProvider(myMachine.state);
if (state.inState(Loading)) {
  return ActivityIndicator();
} else if (state.inState(Data)) {
 return MyDataWidget();
} else {
  throw StateError();
}

@bsutton bsutton closed this as completed Mar 10, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants