Skip to content

Commit

Permalink
Fixed an issue with data expressions of root's final nodes being call…
Browse files Browse the repository at this point in the history
…ed twice
  • Loading branch information
Andarist committed Mar 19, 2020
1 parent ed61b16 commit e88aa18
Show file tree
Hide file tree
Showing 3 changed files with 79 additions and 55 deletions.
5 changes: 5 additions & 0 deletions .changeset/modern-pumas-applaud.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'xstate': patch
---

Fixed an issue with data expressions of root's final nodes being called twice.
107 changes: 52 additions & 55 deletions packages/core/src/StateNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -373,9 +373,9 @@ class StateNode<
this.strict = !!this.config.strict;

// TODO: deprecate (entry)
this.onEntry = toArray(this.config.entry || this.config.onEntry).map(
action => toActionObject(action)
);
this.onEntry = toArray(
this.config.entry || this.config.onEntry
).map(action => toActionObject(action));
// TODO: deprecate (exit)
this.onExit = toArray(this.config.exit || this.config.onExit).map(action =>
toActionObject(action)
Expand Down Expand Up @@ -510,14 +510,11 @@ class StateNode<

const transitions = this.transitions;

return (this.__cache.on = transitions.reduce(
(map, transition) => {
map[transition.eventType] = map[transition.eventType] || [];
map[transition.eventType].push(transition as any);
return map;
},
{} as TransitionDefinitionMap<TContext, TEvent>
));
return (this.__cache.on = transitions.reduce((map, transition) => {
map[transition.eventType] = map[transition.eventType] || [];
map[transition.eventType].push(transition as any);
return map;
}, {} as TransitionDefinitionMap<TContext, TEvent>));
}

public get after(): Array<DelayedTransitionDefinition<TContext, TEvent>> {
Expand Down Expand Up @@ -642,21 +639,20 @@ class StateNode<
}

const subStateKeys = keys(stateValue);
const subStateNodes: Array<
StateNode<TContext, any, TEvent>
> = subStateKeys.map(subStateKey => this.getStateNode(subStateKey));
const subStateNodes: Array<StateNode<
TContext,
any,
TEvent
>> = subStateKeys.map(subStateKey => this.getStateNode(subStateKey));

return subStateNodes.concat(
subStateKeys.reduce(
(allSubStateNodes, subStateKey) => {
const subStateNode = this.getStateNode(subStateKey).getStateNodes(
stateValue[subStateKey]
);
subStateKeys.reduce((allSubStateNodes, subStateKey) => {
const subStateNode = this.getStateNode(subStateKey).getStateNodes(
stateValue[subStateKey]
);

return allSubStateNodes.concat(subStateNode);
},
[] as Array<StateNode<TContext, any, TEvent>>
)
return allSubStateNodes.concat(subStateNode);
}, [] as Array<StateNode<TContext, any, TEvent>>)
);
}

Expand Down Expand Up @@ -995,6 +991,10 @@ class StateNode<

const parent = sn.parent!;

if (!parent.parent) {
return events;
}

events.push(
done(sn.id, sn.data), // TODO: deprecate - final states should not emit done events for their own state.
done(
Expand All @@ -1003,17 +1003,15 @@ class StateNode<
)
);

if (parent.parent) {
const grandparent = parent.parent;
const grandparent = parent.parent!;

if (grandparent.type === 'parallel') {
if (
getChildren(grandparent).every(parentNode =>
isInFinalState(transition.configuration, parentNode)
)
) {
events.push(done(grandparent.id, grandparent.data));
}
if (grandparent.type === 'parallel') {
if (
getChildren(grandparent).every(parentNode =>
isInFinalState(transition.configuration, parentNode)
)
) {
events.push(done(grandparent.id, grandparent.data));
}
}

Expand Down Expand Up @@ -1268,15 +1266,12 @@ class StateNode<
? currentState.configuration
: [];

const meta = resolvedConfiguration.reduce(
(acc, stateNode) => {
if (stateNode.meta !== undefined) {
acc[stateNode.id] = stateNode.meta;
}
return acc;
},
{} as Record<string, string>
);
const meta = resolvedConfiguration.reduce((acc, stateNode) => {
if (stateNode.meta !== undefined) {
acc[stateNode.id] = stateNode.meta;
}
return acc;
}, {} as Record<string, string>);

const isDone = isInFinalState(resolvedConfiguration, this);

Expand Down Expand Up @@ -1749,9 +1744,10 @@ class StateNode<
: parent.initialStateNodes;
}

const subHistoryValue = nestedPath<HistoryValue>(parent.path, 'states')(
historyValue
).current;
const subHistoryValue = nestedPath<HistoryValue>(
parent.path,
'states'
)(historyValue).current;

if (isString(subHistoryValue)) {
return [parent.getStateNode(subHistoryValue)];
Expand Down Expand Up @@ -1903,11 +1899,9 @@ class StateNode<
return transition;
}
private formatTransitions(): Array<TransitionDefinition<TContext, TEvent>> {
let onConfig: Array<
TransitionConfig<TContext, EventObject> & {
event: string;
}
>;
let onConfig: Array<TransitionConfig<TContext, EventObject> & {
event: string;
}>;

if (!this.config.on) {
onConfig = [];
Expand All @@ -1932,11 +1926,14 @@ class StateNode<
return arrayified;
})
.concat(
toTransitionConfigArray(WILDCARD, wildcardConfigs as SingleOrArray<
TransitionConfig<TContext, EventObject> & {
event: '*';
}
>)
toTransitionConfigArray(
WILDCARD,
wildcardConfigs as SingleOrArray<
TransitionConfig<TContext, EventObject> & {
event: '*';
}
>
)
)
);
}
Expand Down
22 changes: 22 additions & 0 deletions packages/core/test/final.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -195,4 +195,26 @@ describe('final states', () => {

service.send('REQUEST_SECRET');
});

it("should only call data expression once when entering root's final state", () => {
const spy = jest.fn();
const machine = Machine({
initial: 'start',
states: {
start: {
on: {
FINISH: 'end'
}
},
end: {
type: 'final',
data: spy
}
}
});

const service = interpret(machine).start();
service.send({ type: 'FINISH', value: 1 });
expect(spy).toBeCalledTimes(1);
});
});

0 comments on commit e88aa18

Please sign in to comment.