Skip to content

Commit

Permalink
Fixed an issue with not all actions of initial transitions being exec…
Browse files Browse the repository at this point in the history
…uted (#4357)
  • Loading branch information
Andarist authored Oct 16, 2023
1 parent 81b6eda commit 84c46c1
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 11 deletions.
5 changes: 5 additions & 0 deletions .changeset/grumpy-shrimps-draw.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'xstate': patch
---

Fixed an issue with not all actions of initial transitions resolving to the initial state of the machine itself being executed.
5 changes: 3 additions & 2 deletions packages/core/src/StateMachine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ import {
resolveActionsAndContext,
resolveStateValue,
transitionNode,
isAtomicStateNode
isAtomicStateNode,
getInitialStateNodes
} from './stateUtils.ts';
import type {
AreAllImplementationsAssumedToBeProvided,
Expand Down Expand Up @@ -455,7 +456,7 @@ export class StateMachine<
const nextState = microstep(
[
{
target: [...preInitialState.configuration].filter(isAtomicStateNode),
target: [...getInitialStateNodes(this.root)],
source: this.root,
reenter: true,
actions: [],
Expand Down
27 changes: 18 additions & 9 deletions packages/core/src/stateUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,9 @@ export function getConfiguration(
for (const s of configuration) {
// if previously active, add existing child nodes
if (s.type === 'compound' && (!adjList.get(s) || !adjList.get(s)!.length)) {
getInitialStateNodes(s).forEach((sn) => configurationSet.add(sn));
getInitialStateNodesWithTheirAncestors(s).forEach((sn) =>
configurationSet.add(sn)
);
} else {
if (s.type === 'parallel') {
for (const child of getChildren(s)) {
Expand All @@ -135,7 +137,8 @@ export function getConfiguration(
}

if (!configurationSet.has(child)) {
for (const initialStateNode of getInitialStateNodes(child)) {
const initialStates = getInitialStateNodesWithTheirAncestors(child);
for (const initialStateNode of initialStates) {
configurationSet.add(initialStateNode);
}
}
Expand Down Expand Up @@ -594,9 +597,19 @@ function isHistoryNode(
return stateNode.type === 'history';
}

export function getInitialStateNodes(
export function getInitialStateNodesWithTheirAncestors(
stateNode: AnyStateNode
): Array<AnyStateNode> {
) {
const states = getInitialStateNodes(stateNode);
for (const initialState of states) {
for (const ancestor of getProperAncestors(initialState, stateNode)) {
states.add(ancestor);
}
}
return states;
}

export function getInitialStateNodes(stateNode: AnyStateNode) {
const set = new Set<AnyStateNode>();

function iter(descStateNode: AnyStateNode): void {
Expand All @@ -606,10 +619,6 @@ export function getInitialStateNodes(
set.add(descStateNode);
if (descStateNode.type === 'compound') {
for (const targetStateNode of descStateNode.initial.target) {
for (const a of getProperAncestors(targetStateNode, stateNode)) {
set.add(a);
}

iter(targetStateNode);
}
} else if (descStateNode.type === 'parallel') {
Expand All @@ -621,7 +630,7 @@ export function getInitialStateNodes(

iter(stateNode);

return [...set];
return set;
}
/**
* Returns the child state node from its relative `stateKey`, or throws.
Expand Down
34 changes: 34 additions & 0 deletions packages/core/test/actions.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2170,6 +2170,40 @@ describe('initial actions', () => {
]
`);
});

it('should execute actions of all initial transitions resolving to the initial state value', () => {
const spy = jest.fn();
const machine = createMachine({
initial: {
target: 'a',
actions: () => spy('root')
},
states: {
a: {
initial: {
target: 'a1',
actions: () => spy('inner')
},
states: {
a1: {}
}
}
}
});

createActor(machine).start();

expect(spy.mock.calls).toMatchInlineSnapshot(`
[
[
"root",
],
[
"inner",
],
]
`);
});
});

describe('actions on invalid transition', () => {
Expand Down

0 comments on commit 84c46c1

Please sign in to comment.