Hello,
First of all thank you for your library.
The context
We're trying to implement a multi-step form and we're using a FSM (robot) as the backbone of the form.
One of the features of that form is that data filled in a given step may skip the next one.
For instance :
- User fills the first step of the form
- The user clicks "Next", we use the data to fetch some objects
- if there are multiple objects, we display step2 to select one of these objets
- otherwise, if there are 1 or 0 objects, no need for step2 we go to step3 directly
We implemented this with a mix of invokes and immediate transitions (which we'll call intermediary steps), that always end up onto a concrete step (a "final ui state").
Also since we're evolving in a web environment, we're synchronizing the current step of the form with the url.
To achieve this, we're listening to the onChange callback and whenever a new state is reached, if it is a concrete step (not an intermediary one used to fetch information) we change the url to that step's one (ie: /step1, /step2).
The issue
However, it seems that the onChange callback is called multiple times for the same concrete step and we're struggling to understand why.
import { createMachine, guard, immediate, invoke, reduce, state, transition, interpret, d } from 'https://unpkg.com/robot3';
async function loadOptions() {
return false ? [] : [{ id: 1 }, { id: 2 }];
}
const machine = createMachine(
{
step1: state(
transition(
'next',
'step1loading',
guard(ctx => ctx.form.valid)
)
),
// Loading available options
step1loading: invoke(
loadOptions,
transition(
'done',
'step1decider',
reduce((ctx, evt) => ({ ...ctx, options: evt.data }))
),
transition('error', 'error')
),
// Deciding which step to go to next
step1decider: state(
immediate(
'step2',
guard(ctx => ctx.options.length > 1)
),
immediate('step3')
),
step2: state(),
step3: state(),
error: state()
},
context => context
);
const service = interpret(
machine,
serv => {
console.log(serv.machine.current);
},
{
form: {
valid: true
},
options: []
}
);
service.send('next');
The following is logged
"step1loading" <-- OK
"step2" <-- Expected (even if step1decider is not there)
"step2" <-- Unexpected
Is it normal behaviour ? If so, by any chance, would you have guidance on how we should handle this ?
Is it an issue and the first onChange call should be done with the step1decider state instead of step2 ? Or should one of the occurence not happen at all ?
The "investigation"
Some work has been done around onChange and immediate transitions here and within the transitionTo call here but we're not familiar enough with the library to point a problem there.
Hello,
First of all thank you for your library.
The context
We're trying to implement a multi-step form and we're using a FSM (robot) as the backbone of the form.
One of the features of that form is that data filled in a given step may skip the next one.
For instance :
We implemented this with a mix of
invokes andimmediatetransitions (which we'll call intermediary steps), that always end up onto a concrete step (a "final ui state").Also since we're evolving in a web environment, we're synchronizing the current step of the form with the url.
To achieve this, we're listening to the
onChangecallback and whenever a new state is reached, if it is a concrete step (not an intermediary one used to fetch information) we change the url to that step's one (ie:/step1,/step2).The issue
However, it seems that the
onChangecallback is called multiple times for the same concrete step and we're struggling to understand why.The following is logged
Is it normal behaviour ? If so, by any chance, would you have guidance on how we should handle this ?
Is it an issue and the first
onChangecall should be done with thestep1deciderstate instead ofstep2? Or should one of the occurence not happen at all ?The "investigation"
Some work has been done around
onChangeandimmediatetransitions here and within thetransitionTocall here but we're not familiar enough with the library to point a problem there.