-
Notifications
You must be signed in to change notification settings - Fork 92
Description
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 ofinvoke
s andimmediate
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.