Skip to content

Commit

Permalink
Pass self to context factories (#4616)
Browse files Browse the repository at this point in the history
  • Loading branch information
Andarist authored Dec 22, 2023
1 parent 6b74bbe commit e8c0b15
Show file tree
Hide file tree
Showing 6 changed files with 86 additions and 8 deletions.
17 changes: 17 additions & 0 deletions .changeset/red-terms-stare.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
---
'xstate': minor
---

`context` factories receive `self` now so you can immediately pass that as part of the input to spawned actors.

```ts
setup({
/* ... */
}).createMachine({
context: ({ spawn, self }) => {
return {
childRef: spawn('child', { input: { parent: self } })
};
}
});
```
4 changes: 2 additions & 2 deletions packages/core/src/StateMachine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -372,8 +372,8 @@ export class StateMachine<
);

if (typeof context === 'function') {
const assignment = ({ spawn, event }: any) =>
context({ spawn, input: event.input });
const assignment = ({ spawn, event, self }: any) =>
context({ spawn, input: event.input, self });
return resolveActionsAndContext(
preInitial,
initEvent,
Expand Down
5 changes: 5 additions & 0 deletions packages/core/src/interpreter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -697,6 +697,11 @@ export class Actor<TLogic extends AnyActorLogic>
* @see {@link Actor.getPersistedSnapshot} to persist the internal state of an actor (which is more than just a snapshot).
*/
public getSnapshot(): SnapshotFrom<TLogic> {
if (isDevelopment && !this._snapshot) {
throw new Error(
`Snapshot can't be read while the actor initializes itself`
);
}
return this._snapshot;
}
}
Expand Down
32 changes: 26 additions & 6 deletions packages/core/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1302,14 +1302,34 @@ export type MachineImplementations<
type InitialContext<
TContext extends MachineContext,
TActor extends ProvidedActor,
TInput
> = TContext | ContextFactory<TContext, TActor, TInput>;
TInput,
TEvent extends EventObject
> = TContext | ContextFactory<TContext, TActor, TInput, TEvent>;

export type ContextFactory<
TContext extends MachineContext,
TActor extends ProvidedActor,
TInput
> = ({ spawn, input }: { spawn: Spawner<TActor>; input: TInput }) => TContext;
TInput,
TEvent extends EventObject = EventObject
> = ({
spawn,
input,
self
}: {
spawn: Spawner<TActor>;
input: TInput;
self: ActorRef<
MachineSnapshot<
TContext,
TEvent,
Record<string, AnyActorRef | undefined>, // TODO: this should be replaced with `TChildren`
StateValue,
string,
unknown
>,
TEvent
>;
}) => TContext;

export type MachineConfig<
TContext extends MachineContext,
Expand Down Expand Up @@ -1358,8 +1378,8 @@ export type MachineConfig<
output?: Mapper<TContext, DoneStateEvent, TOutput, TEvent> | TOutput;
}) &
(MachineContext extends TContext
? { context?: InitialContext<LowInfer<TContext>, TActor, TInput> }
: { context: InitialContext<LowInfer<TContext>, TActor, TInput> });
? { context?: InitialContext<LowInfer<TContext>, TActor, TInput, TEvent> }
: { context: InitialContext<LowInfer<TContext>, TActor, TInput, TEvent> });

export interface ProvidedActor {
src: string;
Expand Down
36 changes: 36 additions & 0 deletions packages/core/test/actor.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1691,4 +1691,40 @@ describe('actors', () => {

expect(actual).toEqual(['stop 1', 'start 2']);
});

it('should be possible to pass `self` as input to a child machine from within the context factory', () => {
const spy = jest.fn();

const child = createMachine({
types: {} as {
context: {
parent: AnyActorRef;
};
input: {
parent: AnyActorRef;
};
},
context: ({ input }) => ({
parent: input.parent
}),
entry: sendTo(({ context }) => context.parent, { type: 'GREET' })
});

const machine = createMachine({
context: ({ spawn, self }) => {
return {
childRef: spawn(child, { input: { parent: self } })
};
},
on: {
GREET: {
actions: spy
}
}
});

createActor(machine).start();

expect(spy).toHaveBeenCalledTimes(1);
});
});
File renamed without changes.

0 comments on commit e8c0b15

Please sign in to comment.