Skip to content

Commit b2f1e8a

Browse files
fix(overmind): add sync transition warning and fix entry transitions
1 parent 01ba71e commit b2f1e8a

File tree

2 files changed

+88
-7
lines changed

2 files changed

+88
-7
lines changed

packages/node_modules/overmind/src/config/statecharts.test.ts

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -683,5 +683,75 @@ describe('Statecharts', () => {
683683
})).toEqual(false)
684684
expect(instance.state.count).toBe(3)
685685
})
686+
687+
test.only('should run entry and exit actions when transitioning within a transition', async () => {
688+
const step: AsyncAction = async ({ state, actions }) => {
689+
state.actionEvents.push('step')
690+
await Promise.resolve()
691+
actions.step2()
692+
}
693+
const step2: Action = ({ state }) => {
694+
state.actionEvents.push('step2')
695+
}
696+
const entry: Action = ({ state }) => {
697+
state.actionEvents.push('entry')
698+
}
699+
const exit: Action = ({ state }) => {
700+
state.actionEvents.push('exit')
701+
}
702+
703+
const config = {
704+
state: {
705+
actionEvents: [] as string[],
706+
},
707+
actions: {
708+
step,
709+
step2,
710+
entry,
711+
exit
712+
},
713+
}
714+
715+
const chart: Statechart<typeof config, {
716+
foo: void
717+
bar: void
718+
baz: void
719+
}> = {
720+
initial: 'foo',
721+
states: {
722+
foo: {
723+
on: {
724+
step: 'bar'
725+
},
726+
exit: 'exit',
727+
},
728+
bar: {
729+
entry: 'entry',
730+
on: {
731+
step2: 'baz'
732+
}
733+
},
734+
baz: {}
735+
},
736+
}
737+
738+
interface Action<Input = void, Output = void>
739+
extends IAction<typeof config, Input, Output> {}
740+
interface AsyncAction<Input = void, Output = void>
741+
extends IAction<typeof config, Input, Promise<Output>> {}
742+
743+
const instance = createOvermind(statecharts(config, {
744+
id1: chart
745+
}))
746+
747+
await instance.actions.step()
748+
749+
expect(instance.state.actionEvents).toEqual([
750+
'exit',
751+
'step',
752+
'entry',
753+
'step2',
754+
])
755+
})
686756

687757
})

packages/node_modules/overmind/src/config/statecharts.ts

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,8 @@ export function statecharts<C extends IConfiguration, Charts extends Statecharts
219219
)
220220
}
221221

222+
let currentTransitionAction: string | null = null
223+
222224
return {
223225
onInitialize: ((context, instance) => {
224226
if (config.onInitialize) {
@@ -294,9 +296,13 @@ export function statecharts<C extends IConfiguration, Charts extends Statecharts
294296
aggr[key] = pipe(
295297
map(function getTransition({ state, execution }: any, payload) {
296298
const stateTarget = getTarget(state, execution.namespacePath)
299+
const canTransition = stateTarget.actions[key]
300+
if (currentTransitionAction && !canTransition) {
301+
console.warn(`Overmind Statecharts: Transition action "${currentTransitionAction}" is calling transition action "${key}" synchronously. The previous transition is not done yet and "${key}" will be ignored. Consider calling it asynchronously `)
302+
}
297303

298304
return {
299-
canTransition: stateTarget.actions[key],
305+
canTransition,
300306
payload,
301307
}
302308
}),
@@ -379,7 +385,7 @@ export function statecharts<C extends IConfiguration, Charts extends Statecharts
379385
if (
380386
target.entry &&
381387
newStatePath[traverseNewPath.length - 1] !==
382-
traverseNewPath[traverseNewPath.length - 1]
388+
stateTarget.states[transitionAction.index][traverseNewPath.length - 1]
383389
) {
384390
entryActions.push(target.entry)
385391
}
@@ -396,21 +402,26 @@ export function statecharts<C extends IConfiguration, Charts extends Statecharts
396402
}
397403
})
398404

399-
// Transition to new state
400-
stateTarget.states = newStates
401-
405+
currentTransitionAction = key
406+
// Run the action that caused the transition
402407
if (config.actions) {
403408
config.actions[key](context, payload)
404409
}
405-
406-
// Run enters
407410

411+
currentTransitionAction = null
412+
413+
// Transition to new state
414+
stateTarget.states = newStates
415+
416+
// Run entry actions
408417
entryActions.forEach((entryAction) => {
409418
if (config.actions) {
410419
config.actions[entryAction](context, payload)
411420
}
412421
})
413422

423+
424+
414425
if (process.env.NODE_ENV === 'development' && currentInstance.devtools) {
415426
currentInstance.devtools.send({
416427
type: 'chart',

0 commit comments

Comments
 (0)