Skip to content

Commit

Permalink
[v5] Require context if specified (#4049)
Browse files Browse the repository at this point in the history
* Require machine context if defined

* Oops

* Update packages/core/src/types.ts

Co-authored-by: Mateusz Burzyński <mateuszburzynski@gmail.com>

* Okay Svelte

* Fix types

* Fix tests

* Add changeset

* Revert irrelevant files

---------

Co-authored-by: Mateusz Burzyński <mateuszburzynski@gmail.com>
  • Loading branch information
davidkpiano and Andarist committed Jul 15, 2023
1 parent 2f36232 commit afc6900
Show file tree
Hide file tree
Showing 29 changed files with 376 additions and 123 deletions.
25 changes: 25 additions & 0 deletions .changeset/bright-hotels-sleep.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
---
'xstate': major
---

If context types are specified in the machine config, the `context` property will now be required:

```ts
// ❌ TS error
createMachine({
types: {} as {
context: { count: number };
}
// Missing context property
});

// ✅ OK
createMachine({
types: {} as {
context: { count: number };
},
context: {
count: 0
}
});
```
2 changes: 1 addition & 1 deletion .changeset/light-spiders-attend.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
"xstate": patch
'xstate': patch
---

Remove `State['changed']`. A new instance of `State` is being created if there are matching transitions for the received event. If there are no matching transitions then the current state is being returned.
2 changes: 1 addition & 1 deletion .changeset/new-gorillas-visit.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
"xstate": major
'xstate': major
---

Removed `State['transitions']`.
2 changes: 1 addition & 1 deletion .changeset/stale-chairs-rush.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
"xstate": major
'xstate': major
---

Removed `State['event']`.
2 changes: 1 addition & 1 deletion .changeset/young-laws-hope.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
"xstate": major
'xstate': major
---

Removed `State['actions']`. Actions are considered to be a side-effect of a transition, things that happen in the moment and are not meant to be persisted beyond that.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ const toggleMachine = createMachine({

// Machine instance with internal state
const toggleActor = interpret(toggleMachine);
toggleActor.subscribe((state) => console.log(state.value))
toggleActor.subscribe((state) => console.log(state.value));
toggleActor.start();
// => logs 'inactive'

Expand Down
13 changes: 5 additions & 8 deletions docs/recipes/ember.md
Original file line number Diff line number Diff line change
Expand Up @@ -135,15 +135,12 @@ export default class extends Component {
```

```hbs
<this.MyLocalMachine as |state send|>
</this.MyLocalMachine>
<this.MyLocalMachine as |state send| />
<this.CustomMachine as |state send|>
</this.CustomMachine>
<this.CustomMachine as |state send| />
```

If using Ember versions older than [Ember 4.5](https://blog.emberjs.com/ember-4-5-released), it is recommended to also have the [plain functions polyfill](https://github.com/ember-polyfills/ember-functions-as-helper-polyfill)
If using Ember versions older than [Ember 4.5](https://blog.emberjs.com/ember-4-5-released), it is recommended to also have the [plain functions polyfill](https://github.com/ember-polyfills/ember-functions-as-helper-polyfill)
installed so that you can use `state.matches(...)` and other XState-provided APIs from the template.

## Custom integration
Expand All @@ -163,8 +160,8 @@ This example is based on Ember Octane features (Ember 3.13+)
:::

```handlebars
<button type="button" {{on "click" (fn this.transition "TOGGLE")}}>
{{if this.isInactive "Off" "On"}}
<button type='button' {{on 'click' (fn this.transition 'TOGGLE')}}>
{{if this.isInactive 'Off' 'On'}}
</button>
```

Expand Down
15 changes: 6 additions & 9 deletions docs/recipes/vue.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Usage of XState in a Vue application may vary depending on which version of Vue

There are two ways you can use XState with Vue 2:

1. *Since Vue `^2.7`*: using the `useMachine` hook provided by the [`xstate-vue2` package](https://github.com/ChrisShank/xstate-vue2) (3rd-party) plugin;
1. _Since Vue `^2.7`_: using the `useMachine` hook provided by the [`xstate-vue2` package](https://github.com/ChrisShank/xstate-vue2) (3rd-party) plugin;
2. Using XState `interpret` utility to create a service and inject it into your app.

::: tip
Expand Down Expand Up @@ -53,20 +53,17 @@ export const toggleMachine = createMachine({
<!-- toggle.vue -->
<!-- Top level bindigs are pre-processed via "setup" -->
<script setup>
import { useMachine } from "xstate-vue2";
import toggleMachine from "../path/to/toggleMachine";
import { useMachine } from 'xstate-vue2';
import toggleMachine from '../path/to/toggleMachine';
const { state, send } = useMachine(toggleMachine);
const { state, send } = useMachine(toggleMachine);
</script>

<template>
<main>
<button @click="send('TOGGLE')">
{{
state.value === "inactive"
? "Click to activate"
: "Active! Click to deactivate"
}}
{{ state.value === "inactive" ? "Click to activate" : "Active! Click to
deactivate" }}
</button>
</main>
</template>
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/Machine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export function createMachine<
ResolveTypegenMeta<TTypesMeta, TEvent, ParameterizedObject, TActorMap>
> {
return new StateMachine<any, any, any, any, any>(
config,
config as any,
implementations as any
);
}
10 changes: 6 additions & 4 deletions packages/core/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -906,23 +906,25 @@ export type ContextFactory<TContext extends MachineContext> = ({
input: any; // TODO: fix
}) => TContext;

export interface MachineConfig<
export type MachineConfig<
TContext extends MachineContext,
TEvent extends EventObject,
TAction extends ParameterizedObject = ParameterizedObject,
TActorMap extends ActorMap = ActorMap,
TTypesMeta = TypegenDisabled
> extends StateNodeConfig<NoInfer<TContext>, NoInfer<TEvent>, TAction> {
> = (StateNodeConfig<NoInfer<TContext>, NoInfer<TEvent>, TAction> & {
/**
* The initial context (extended state)
*/
context?: InitialContext<LowInfer<TContext>>;
/**
* The machine's own version.
*/
version?: string;
types?: MachineTypes<TContext, TEvent, TActorMap, TTypesMeta>;
}
}) &
(Equals<TContext, MachineContext> extends true
? { context?: InitialContext<LowInfer<TContext>> }
: { context: InitialContext<LowInfer<TContext>> });

export type ActorMap = Record<string, { output: any }>;
export interface MachineTypes<
Expand Down
69 changes: 51 additions & 18 deletions packages/core/test/actions.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2461,7 +2461,13 @@ describe('purely defined actions', () => {

describe('forwardTo()', () => {
it('should forward an event to a service', (done) => {
const child = createMachine<any, { type: 'EVENT'; value: number }>({
const child = createMachine({
types: {} as {
events: {
type: 'EVENT';
value: number;
};
},
id: 'child',
initial: 'active',
states: {
Expand All @@ -2476,10 +2482,17 @@ describe('forwardTo()', () => {
}
});

const parent = createMachine<
any,
{ type: 'EVENT'; value: number } | { type: 'SUCCESS' }
>({
const parent = createMachine({
types: {} as {
events:
| {
type: 'EVENT';
value: number;
}
| {
type: 'SUCCESS';
};
},
id: 'parent',
initial: 'first',
states: {
Expand All @@ -2506,7 +2519,13 @@ describe('forwardTo()', () => {
});

it('should forward an event to a service (dynamic)', (done) => {
const child = createMachine<any, { type: 'EVENT'; value: number }>({
const child = createMachine({
types: {} as {
events: {
type: 'EVENT';
value: number;
};
},
id: 'child',
initial: 'active',
states: {
Expand Down Expand Up @@ -2934,12 +2953,14 @@ describe('choose', () => {
describe('sendParent', () => {
// https://github.com/statelyai/xstate/issues/711
it('TS: should compile for any event', () => {
interface ChildContext {}
interface ChildEvent {
type: 'CHILD';
}

const child = createMachine<ChildContext, ChildEvent>({
const child = createMachine({
types: {} as {
events: ChildEvent;
},
id: 'child',
initial: 'start',
states: {
Expand All @@ -2956,7 +2977,10 @@ describe('sendParent', () => {

describe('sendTo', () => {
it('should be able to send an event to an actor', (done) => {
const childMachine = createMachine<any, { type: 'EVENT' }>({
const childMachine = createMachine({
types: {} as {
events: { type: 'EVENT' };
},
initial: 'waiting',
states: {
waiting: {
Expand All @@ -2981,7 +3005,10 @@ describe('sendTo', () => {
});

it('should be able to send an event from expression to an actor', (done) => {
const childMachine = createMachine<any, { type: 'EVENT'; count: number }>({
const childMachine = createMachine({
types: {} as {
events: { type: 'EVENT'; count: number };
},
initial: 'waiting',
states: {
waiting: {
Expand Down Expand Up @@ -3011,7 +3038,10 @@ describe('sendTo', () => {
});

it('should report a type error for an invalid event', () => {
const childMachine = createMachine<any, { type: 'EVENT' }>({
const childMachine = createMachine({
types: {} as {
events: { type: 'EVENT' };
},
initial: 'waiting',
states: {
waiting: {
Expand All @@ -3036,7 +3066,10 @@ describe('sendTo', () => {
});

it('should be able to send an event to a named actor', (done) => {
const childMachine = createMachine<any, { type: 'EVENT' }>({
const childMachine = createMachine({
types: {} as {
events: { type: 'EVENT' };
},
initial: 'waiting',
states: {
waiting: {
Expand All @@ -3063,7 +3096,10 @@ describe('sendTo', () => {
});

it('should be able to send an event directly to an ActorRef', (done) => {
const childMachine = createMachine<any, { type: 'EVENT' }>({
const childMachine = createMachine({
types: {} as {
events: { type: 'EVENT' };
},
initial: 'waiting',
states: {
waiting: {
Expand Down Expand Up @@ -3093,11 +3129,8 @@ describe('sendTo', () => {
it('should be able to read from event', () => {
expect.assertions(1);
const machine = createMachine({
types: {
events: {} as {
type: 'EVENT';
value: string;
}
types: {} as {
events: { type: 'EVENT'; value: string };
},
initial: 'a',
context: ({ spawn }) => ({
Expand Down
5 changes: 4 additions & 1 deletion packages/core/test/actor.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,10 @@ describe('spawning machines', () => {
| { type: 'PONG' }
| { type: 'SUCCESS' };

const serverMachine = createMachine<any, PingPongEvent>({
const serverMachine = createMachine({
types: {} as {
events: PingPongEvent;
},
id: 'server',
initial: 'waitPing',
states: {
Expand Down
6 changes: 5 additions & 1 deletion packages/core/test/guards.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,16 @@ describe('guard conditions', () => {
| { type: 'TIMER_COND_OBJ' }
| { type: 'BAD_COND' };

const lightMachine = createMachine<LightMachineCtx, LightMachineEvents>(
const lightMachine = createMachine(
{
context: ({ input = {} }) => ({
elapsed: input.elapsed || 0
}),
initial: 'green',
types: {} as {
context: LightMachineCtx;
events: LightMachineEvents;
},
states: {
green: {
on: {
Expand Down
11 changes: 7 additions & 4 deletions packages/core/test/interpreter.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -911,10 +911,13 @@ describe('interpreter', () => {
}
});

const parentMachine = createMachine<
any,
{ type: 'NEXT'; password: string }
>({
const parentMachine = createMachine({
types: {} as {
events: {
type: 'NEXT';
password: string;
};
},
id: 'parent',
initial: 'start',
states: {
Expand Down
Loading

0 comments on commit afc6900

Please sign in to comment.