I have created this codesandbox to highlight the problem.
I have created a state machine for a react hook abortable fetch.
The only way I could get it to work was to have all the states at the same level:
export interface AbortableSchema {
states: {
[AbortableStates.Idle]: {};
[AbortableStates.Loading]: {};
[AbortableStates.Succeded]: {};
[AbortableStates.Error]: {};
[AbortableStates.Aborted]: {};
};
}
export const createAbortableMachine = <D>(): StateMachine<
AbortableState<D>,
AbortableSchema,
AbortableActions<D>
> => {
const context: AbortableState<D> = {
state: AbortableStates.Idle,
data: undefined,
error: undefined
};
const abortableMachine = Machine<
AbortableState<D>,
AbortableSchema,
AbortableActions<D>
>({
id: "fetchable",
initial: AbortableStates.Idle,
context,
states: {
[AbortableStates.Idle]: {
on: { START: AbortableStates.Loading }
},
[AbortableStates.Loading]: {
on: {
[AbortableActionTypes.Success]: {
target: AbortableStates.Succeded,
actions: (context, event) => {
context.data = { ...event.payload };
}
},
[AbortableActionTypes.Error]: {
target: [AbortableStates.Error],
actions: (context, event) => {
context.error = { ...event.error };
}
},
[AbortableActionTypes.Abort]: {
target: [AbortableStates.Aborted]
}
}
},
[AbortableStates.Succeded]: {
on: {
[AbortableActionTypes.Reset]: {
target: AbortableStates.Idle,
actions: (_context, event) => {
_context = context;
return _context;
}
}
}
},
[AbortableStates.Error]: {
on: {
[AbortableActionTypes.Reset]: {
target: AbortableStates.Idle,
actions: (_context, event) => {
_context = context;
return _context;
}
}
}
},
[AbortableStates.Aborted]: {
on: {
[AbortableActionTypes.Reset]: {
target: AbortableStates.Idle,
actions: (_context, event) => {
_context = context;
return _context;
}
}
}
}
}
});
return abortableMachine;
};
But to me, it makes more sense to nest the states like this:
export interface AbortableSchema {
states: {
[AbortableStates.Idle]: {};
[AbortableStates.Loading]: {
states: {
[AbortableStates.Succeded]: {};
[AbortableStates.Error]: {};
[AbortableStates.Aborted]: {};
};
};
};
}
export const createAbortableMachine = <D>(): StateMachine<
AbortableState<D>,
AbortableSchema,
AbortableActions<D>
> => {
const context: AbortableState<D> = {
state: AbortableStates.Idle,
data: undefined,
error: undefined
};
const abortableMachine = Machine<
AbortableState<D>,
AbortableSchema,
AbortableActions<D>
>({
id: "fetchable",
initial: AbortableStates.Idle,
context,
states: {
[AbortableStates.Idle]: {
on: { START: AbortableStates.Loading }
},
[AbortableStates.Loading]: {
on: {
[AbortableActionTypes.Success]: {
target: AbortableStates.Succeded,
actions: (context, event) => {
context.data = { ...event.payload };
}
},
[AbortableActionTypes.Error]: {
target: [AbortableStates.Error],
actions: (context, event) => {
context.error = { ...event.error };
}
},
[AbortableActionTypes.Abort]: {
target: [AbortableStates.Aborted]
}
},
states: {
[AbortableStates.Succeded]: {
on: {
[AbortableActionTypes.Reset]: {
target: AbortableStates.Idle,
actions: (_context, event) => {
_context = context;
return _context;
}
}
}
},
[AbortableStates.Error]: {
on: {
[AbortableActionTypes.Reset]: {
target: AbortableStates.Idle,
actions: (_context, event) => {
_context = context;
return _context;
}
}
}
},
[AbortableStates.Aborted]: {
on: {
[AbortableActionTypes.Reset]: {
target: AbortableStates.Idle,
actions: (_context, event) => {
_context = context;
return _context;
}
}
}
}
}
}
}
});
return abortableMachine;
};
Loading has child states but if I do that I get the following error message:
Invalid transition definition for state node 'fetchable.loading': Child state 'succeeded' does not exist on 'fetchable'