Skip to content

Commit

Permalink
Remove initial state getter from machines and require actor context f…
Browse files Browse the repository at this point in the history
…or `transition`/`getInitialState` calls (#4018)

* Remove initialState getter

* add changesets
  • Loading branch information
Andarist committed Jun 1, 2023
1 parent dc006e7 commit c59bb6a
Show file tree
Hide file tree
Showing 62 changed files with 3,015 additions and 2,419 deletions.
5 changes: 5 additions & 0 deletions .changeset/happy-kings-destroy.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'xstate': major
---

`machine.initialState` has been removed, you can use `machine.getInitialState(...)` instead
5 changes: 5 additions & 0 deletions .changeset/itchy-owls-swim.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'xstate': major
---

`machine.transition(...)` and `machine.getInitialState(...)` require now an `actorContext` argument
5 changes: 5 additions & 0 deletions .changeset/odd-zoos-heal.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'xstate': major
---

`machine.transition` no longer accepts state values. You have to resolve the state value to a `State` before passing it to `machine.transition`
30 changes: 8 additions & 22 deletions packages/core/src/StateMachine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -231,22 +231,19 @@ export class StateMachine<
* @param event The received event
*/
public transition(
state: State<TContext, TEvent, TResolvedTypesMeta> | StateValue = this
.initialState,
state: State<TContext, TEvent, TResolvedTypesMeta>,
event: TEvent,
actorCtx?: ActorContext<TEvent, State<TContext, TEvent, any>>
actorCtx: ActorContext<TEvent, State<TContext, TEvent, any>>
): State<TContext, TEvent, TResolvedTypesMeta> {
const currentState =
state instanceof State ? state : this.resolveStateValue(state);
// TODO: handle error events in a better way
if (
isErrorEvent(event) &&
!currentState.nextEvents.some((nextEvent) => nextEvent === event.type)
!state.nextEvents.some((nextEvent) => nextEvent === event.type)
) {
throw event.data;
}

const { state: nextState } = macrostep(currentState, event, actorCtx);
const { state: nextState } = macrostep(state, event, actorCtx);

return nextState;
}
Expand All @@ -259,9 +256,9 @@ export class StateMachine<
* @param event The received event
*/
public microstep(
state: State<TContext, TEvent, TResolvedTypesMeta> = this.initialState,
state: State<TContext, TEvent, TResolvedTypesMeta>,
event: TEvent,
actorCtx?: AnyActorContext | undefined
actorCtx: AnyActorContext
): Array<State<TContext, TEvent, TResolvedTypesMeta>> {
return macrostep(state, event, actorCtx).microstates;
}
Expand All @@ -278,7 +275,7 @@ export class StateMachine<
* This "pre-initial" state is provided to initial actions executed in the initial state.
*/
private getPreInitialState(
actorCtx: AnyActorContext | undefined,
actorCtx: AnyActorContext,
input: any
): State<TContext, TEvent, TResolvedTypesMeta> {
const [context, actions] = this.getContextAndActions(actorCtx, input);
Expand Down Expand Up @@ -312,22 +309,11 @@ export class StateMachine<
return preInitial;
}

/**
* The initial State instance, which includes all actions to be executed from
* entering the initial state.
*/
public get initialState(): State<TContext, TEvent, TResolvedTypesMeta> {
return this.getInitialState();
}

/**
* Returns the initial `State` instance, with reference to `self` as an `ActorRef`.
*/
public getInitialState(
actorCtx?: ActorContext<
TEvent,
State<TContext, TEvent, TResolvedTypesMeta>
>,
actorCtx: ActorContext<TEvent, State<TContext, TEvent, TResolvedTypesMeta>>,
input?: any
): State<TContext, TEvent, TResolvedTypesMeta> {
const initEvent = createInitEvent(input) as unknown as TEvent; // TODO: fix;
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/actors/callback.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export interface CallbackInternalState {

export function fromCallback<TEvent extends EventObject>(
invokeCallback: InvokeCallback
): ActorBehavior<TEvent, undefined> {
): ActorBehavior<TEvent, undefined, CallbackInternalState> {
const behavior: ActorBehavior<TEvent, undefined, CallbackInternalState> = {
config: invokeCallback,
start: (_state, { self }) => {
Expand Down
7 changes: 6 additions & 1 deletion packages/core/src/actors/observable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,12 @@ export function fromObservable<T, TEvent extends EventObject>(

export function fromEventObservable<T extends EventObject>(
lazyObservable: ({ input }: { input: any }) => Subscribable<T>
): ActorBehavior<EventObject, T | undefined> {
): ActorBehavior<
EventObject,
T | undefined,
ObservableInternalState<T>,
ObservablePersistedState<T>
> {
const errorEventType = '$$xstate.error';
const completeEventType = '$$xstate.complete';

Expand Down
75 changes: 0 additions & 75 deletions packages/core/src/patterns.ts

This file was deleted.

20 changes: 9 additions & 11 deletions packages/core/src/stateUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,14 +46,12 @@ import {
AnyEventObject,
AnyHistoryValue,
AnyState,
AnyStateMachine,
AnyStateNode,
AnyTransitionDefinition,
DelayedTransitionDefinition,
HistoryValue,
InitialTransitionDefinition,
SendActionObject,
StateFromMachine
SendActionObject
} from '.';
import { stopSignalType } from './actors/index.ts';
import { ActorStatus } from './interpreter.ts';
Expand Down Expand Up @@ -1031,7 +1029,7 @@ export function microstep<
>(
transitions: Array<TransitionDefinition<TContext, TEvent>>,
currentState: State<TContext, TEvent, any>,
actorCtx: AnyActorContext | undefined,
actorCtx: AnyActorContext,
event: TEvent
): State<TContext, TEvent, any> {
const { machine } = currentState;
Expand Down Expand Up @@ -1120,7 +1118,7 @@ function microstepProcedure(
currentState: AnyState,
mutConfiguration: Set<AnyStateNode>,
event: AnyEventObject,
actorCtx: AnyActorContext | undefined
actorCtx: AnyActorContext
): typeof currentState {
const actions: BaseActionObject[] = [];
const historyValue = {
Expand Down Expand Up @@ -1541,10 +1539,10 @@ export function resolveActionsAndContext<
};
}

export function macrostep<TMachine extends AnyStateMachine>(
state: StateFromMachine<TMachine>,
event: TMachine['__TEvent'],
actorCtx: AnyActorContext | undefined
export function macrostep(
state: AnyState,
event: EventObject,
actorCtx: AnyActorContext
): {
state: typeof state;
microstates: Array<typeof state>;
Expand All @@ -1554,7 +1552,7 @@ export function macrostep<TMachine extends AnyStateMachine>(
}

let nextState = state;
const states: StateFromMachine<TMachine>[] = [];
const states: AnyState[] = [];

// Handle stop event
if (event.type === stopSignalType) {
Expand Down Expand Up @@ -1630,7 +1628,7 @@ export function macrostep<TMachine extends AnyStateMachine>(
function stopStep(
event: AnyEventObject,
nextState: AnyState,
actorCtx: AnyActorContext | undefined
actorCtx: AnyActorContext
): AnyState {
const actions: BaseActionObject[] = [];

Expand Down
23 changes: 17 additions & 6 deletions packages/core/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,13 @@ export type Spawner = <T extends ActorBehavior<any, any> | string>( // TODO: rea
systemId?: string;
input: any;
}>
) => T extends ActorBehavior<infer TActorEvent, infer TActorEmitted>
) => T extends ActorBehavior<
infer TActorEvent,
infer TActorEmitted,
infer _,
infer __,
infer ___
>
? ActorRef<TActorEvent, TActorEmitted>
: ActorRef<any, any>; // TODO: narrow this to behaviors from machine

Expand Down Expand Up @@ -1758,7 +1764,7 @@ export type AnyActorContext = ActorContext<any, any, any>;
export interface ActorBehavior<
TEvent extends EventObject,
TSnapshot = any,
TInternalState = any,
TInternalState = TSnapshot,
/**
* Serialized internal state used for persistence & restoration
*/
Expand Down Expand Up @@ -1820,7 +1826,15 @@ export type SnapshotFrom<T> = ReturnTypeOrValue<T> extends infer R
: never;

export type EventFromBehavior<TBehavior extends ActorBehavior<any, any>> =
TBehavior extends ActorBehavior<infer TEvent, infer _> ? TEvent : never;
TBehavior extends ActorBehavior<
infer TEvent,
infer _,
infer __,
infer ___,
infer ____
>
? TEvent
: never;

export type PersistedStateFrom<TBehavior extends ActorBehavior<any, any>> =
TBehavior extends ActorBehavior<
Expand Down Expand Up @@ -1896,9 +1910,6 @@ export type TagsFrom<TMachine extends AnyStateMachine> = Parameters<
StateFrom<TMachine>['hasTag']
>[0];

export type StateFromMachine<TMachine extends AnyStateMachine> =
TMachine['initialState'];

export interface ActorSystemInfo {
actors: Record<string, AnyActorRef>;
}
Expand Down
12 changes: 7 additions & 5 deletions packages/core/test/actions.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -913,8 +913,8 @@ describe('entry/exit actions', () => {

const flushTracked = trackEntries(machine);

const aa1State = machine.resolveStateValue({ A: 'A1' });
const service = interpret(machine, { state: aa1State }).start();
const service = interpret(machine).start();
flushTracked();
service.send({ type: 'NEXT' });

expect(flushTracked()).toEqual([
Expand Down Expand Up @@ -2231,9 +2231,11 @@ describe('actions config', () => {
}
}
);
const state = machine.transition('a', { type: 'EVENT' });
const actorRef = interpret(machine).start();
actorRef.send({ type: 'EVENT' });
const snapshot = actorRef.getSnapshot();

// expect(state.actions).toEqual([
// expect(snapshot.actions).toEqual([
// expect.objectContaining({
// type: 'definedAction'
// }),
Expand All @@ -2243,7 +2245,7 @@ describe('actions config', () => {
// ]);
// TODO: specify which actions other actions came from

expect(state.context).toEqual({ count: 10 });
expect(snapshot.context).toEqual({ count: 10 });
});

it('should work with anonymous functions (with warning)', () => {
Expand Down
8 changes: 4 additions & 4 deletions packages/core/test/actor.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -849,10 +849,10 @@ describe('actors', () => {
}
});

const { initialState } = nullActorMachine;

// expect(initialState.context.ref!.id).toBe('null'); // TODO: identify null actors
expect(initialState.context.ref!.send).toBeDefined();
// expect(interpret(nullActorMachine).getSnapshot().context.ref!.id).toBe('null'); // TODO: identify null actors
expect(
interpret(nullActorMachine).getSnapshot().context.ref!.send
).toBeDefined();
});

describe('with behaviors', () => {
Expand Down
Loading

0 comments on commit c59bb6a

Please sign in to comment.