Skip to content

Commit

Permalink
[v5] Explicit spawn (#3148)
Browse files Browse the repository at this point in the history
* All tests pass!

* Fix all tests

* Some types cleanup

* Fix types WIP

* Add TODOs for types

* Skip questionable test

* Remove stray console logs

* Add `spawn.machine`

* Add `spawn.promise`

* Add `spawn.observable`

* Add tests for referenced behaviors

* Revert "Add `spawn.observable`"

This reverts commit 6df48b5.

* Revert "Add `spawn.promise`"

This reverts commit 9fce5a9.

* Revert "Add `spawn.machine`"

This reverts commit 0a3ac9f.

* Cleanup

* Temp fix for string references

* Ignore excessive stack depth warnings

* This actually passes

* Remove invoke*

* Rename createDeferredBehavior -> createCallbackBehavior

* Make getContextAndActions private

* Fix & clarify unskipped test

* Add test from todo

* Clean up test

* Make parent "private"

* Make sure children is "immutable" (+ todos)

* Revert initialState change

* Add comment

* Oops

* Remove invoke* functions (except for invokeMachine)

* Quell 1 type error

* Fixed the conditional type in the `interpret`

* Bring back removed `spawn` type test

* Tweak one `assign` test

* Remove lazy argument from `createMachineBehavior` and remove `invokeMachine` entirely

* Add a TODO comment about hiding `ActorRef["_parent"]`

* Require a machine with all implementations provided as an argument for `createMachineBehavior`

* Cache the `initialState` getter in `createMachineBehavior`

* Unwrap `createCallbackBehavior` argument from a redundant lazy layer

* Renaming WIP

* Renaming WIP

* Add back spawnBehavior but call it createActorRef

* behaviors -> actors

* Tweak tests

* Remove Actor class

* Remove actor.ts

* Fix missing function

* Remove createBehaviorFrom

* Make behavior private

* Add onEmit and remove complete

* Tighten up types (a little)

* Fix types

* Remove createBehaviorFrom

* No more magic events

* Update packages/core/src/actors.ts

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

* Update packages/core/src/types.ts

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

* Fix types

* Fix sendError (send raw error data, not events)

* Attempt to fix type

* Fix type

* Rename onEmit -> onSnapshot

* Rename types: TEmitted -> TSnapshot

* Change src to allow behaviors directly (WIP)

* Fix type errors

* Add back InvokeSrcDefinition for now

* Add fromEventObservable

* Remove outdated `xstate/behaviors` references (#3280)

* Avoid wrapping `behaviorImpl` in a throwaway function (#3281)

* Update packages/core/test/actions.test.ts

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

* Update packages/xstate-svelte/src/useSelector.ts

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

* Update packages/xstate-svelte/src/useSelector.ts

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

* Update packages/xstate-vue/src/useSelector.ts

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

* Update packages/xstate-vue/src/useSelector.ts

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

* Remove unused type

* Remove subscriptions from behaviors (they belong on ObservableActorRef only)

* Add TODO

* Remove unused type

* Refactor: do not use observers for invoked promises

* Remove latestData logic in interpreter

* Remove observer logic from fromObservable/fromObservableEvent

* Remove observers

* Remove subscription logic from interpreter

* Remove unused type

* Fixed `ObservableActorRef` subscriptions and remove some outdated/redundant code (#3319)

* Fixed `ObservableActorRef` subscriptions and remove some outdated/redundant code

* Add changesets

* Remove `createActorRef`

* Make `Interpreter#nextState` pure again (#3340)

* Rename `EmittedFrom` to `SnapshotFrom` (#3341)

* Add changesets

Co-authored-by: Mateusz Burzyński <mateuszburzynski@gmail.com>
  • Loading branch information
davidkpiano and Andarist committed May 26, 2022
1 parent dc3eba3 commit 7a68cbb
Show file tree
Hide file tree
Showing 73 changed files with 2,425 additions and 2,408 deletions.
5 changes: 5 additions & 0 deletions .changeset/curvy-feet-guess.md
@@ -0,0 +1,5 @@
---
'xstate': minor
---

`onSnapshot` is now available for invoke configs. You can specify a transition there to be taken when a snapshot of an invoked actor gets updated. It works similarly to `onDone`/`onError`.
26 changes: 0 additions & 26 deletions .changeset/green-apes-heal.md

This file was deleted.

5 changes: 5 additions & 0 deletions .changeset/nervous-bananas-cover.md
@@ -0,0 +1,5 @@
---
'@xstate/vue': major
---

Removed `getSnapshot` parameter from composables. It is expected that the received `actorRef` has to have a `getSnapshot` method on it that can be used internally.
20 changes: 20 additions & 0 deletions .changeset/plenty-dragons-lie.md
@@ -0,0 +1,20 @@
---
'xstate': major
---

`spawn` is no longer importable from `xstate`. Instead you get it in `assign` like this:

```js
assign((ctx, ev, { spawn }) => {
return {
...ctx,
actorRef: spawn(promiseActor)
};
});
```

In addition to that, you can now `spawn` actors defined in your implementations object, in the same way that you were already able to do that with `invoke`. To do that just reference the defined actor like this:

```js
spawn('promiseActor');
```
5 changes: 5 additions & 0 deletions .changeset/quiet-peaches-joke.md
@@ -0,0 +1,5 @@
---
'xstate': major
---

`EmittedFrom` type helper has been renamed to `SnapshotFrom`.
77 changes: 0 additions & 77 deletions .changeset/rotten-rivers-wonder.md
Expand Up @@ -2,85 +2,8 @@
'xstate': major
---

**Breaking:** Activities are no longer a separate concept. Instead, activities are invoked. Internally, this is how activities worked. The API is consolidated so that `activities` are no longer a property of the state node or machine options:

```diff
import { createMachine } from 'xstate';
+import { invokeActivity } from 'xstate/invoke';

const machine = createMachine(
{
// ...
- activities: 'someActivity',
+ invoke: {
+ src: 'someActivity'
+ }
},
{
- activities: {
+ actors: {
- someActivity: ((context, event) => {
+ someActivity: invokeActivity((context, event) => {
// ... some continuous activity

return () => {
// dispose activity
}
})
}
}
);
```

**Breaking:** The `services` option passed as the second argument to `createMachine(config, options)` is renamed to `actors`. Each value in `actors`should be a function that takes in `context` and `event` and returns a [behavior](TODO: link) for an actor. The provided invoke creators are:

- `invokeActivity`
- `invokePromise`
- `invokeCallback`
- `invokeObservable`
- `invokeMachine`

```diff
import { createMachine } from 'xstate';
+import { invokePromise } from 'xstate/invoke';

const machine = createMachine(
{
// ...
invoke: {
src: 'fetchFromAPI'
}
},
{
- services: {
+ actors: {
- fetchFromAPI: ((context, event) => {
+ fetchFromAPI: invokePromise((context, event) => {
// ... (return a promise)
})
}
}
);
```

**Breaking:** The `state.children` property is now a mapping of invoked actor IDs to their `ActorRef` instances.

**Breaking:** The way that you interface with invoked/spawned actors is now through `ActorRef` instances. An `ActorRef` is an opaque reference to an `Actor`, which should be never referenced directly.

**Breaking:** The `src` of an `invoke` config is now either a string that references the machine's `options.actors`, or a `BehaviorCreator`, which is a function that takes in `context` and `event` and returns a `Behavior`:

```diff
import { createMachine } from 'xstate';
+import { invokePromise } from 'xstate/invoke';

const machine = createMachine({
// ...
invoke: {
- src: (context, event) => somePromise
+ src: invokePromise((context, event) => somePromise)
}
// ...
});
```

**Breaking:** The `origin` of an `SCXML.Event` is no longer a string, but an `ActorRef` instance.
34 changes: 34 additions & 0 deletions .changeset/rotten-schools-march.md
@@ -0,0 +1,34 @@
---
'xstate': major
---

The `services` option passed as the second argument to `createMachine(config, options)` is renamed to `actors`. Each value in `actors` should be a function that takes in `context` and `event` and returns a [behavior](TODO: link) for an actor. The provided behavior creators are:

- `fromMachine`
- `fromPromise`
- `fromCallback`
- `fromObservable`
- `fromEventObservable`

```diff
import { createMachine } from 'xstate';
+import { fromPromise } from 'xstate/actors';

const machine = createMachine(
{
// ...
invoke: {
src: 'fetchFromAPI'
}
},
{
- services: {
+ actors: {
- fetchFromAPI: (context, event) => {
+ fetchFromAPI: (context, event) => fromPromise(() => {
// ... (return a promise)
})
}
}
);
```
5 changes: 5 additions & 0 deletions .changeset/tricky-spoons-lay.md
@@ -0,0 +1,5 @@
---
'@xstate/react': major
---

Removed `getSnapshot` parameter from hooks. It is expected that the received `actorRef` has to have a `getSnapshot` method on it that can be used internally.
9 changes: 5 additions & 4 deletions docs/packages/xstate-react/index.md
Expand Up @@ -358,7 +358,7 @@ Example: the `'fetchData'` service and `'notifySuccess'` action are both configu

```js
import { createMachine } from 'xstate';
import { invokePromise } from 'xstate/invoke';
import { fromPromise } from 'xstate/actors';

const fetchMachine = createMachine({
id: 'fetch',
Expand Down Expand Up @@ -406,9 +406,10 @@ const Fetcher = ({ onResolve }) => {
notifySuccess: (ctx) => onResolve(ctx.data)
},
actors: {
fetchData: invokePromise((_, event) =>
fetch(`some/api/${event.query}`).then((res) => res.json())
)
fetchData: (_, event) =>
fromPromise(() =>
fetch(`some/api/${event.query}`).then((res) => res.json())
)
}
});

Expand Down
2 changes: 1 addition & 1 deletion packages/core/CHANGELOG.md
Expand Up @@ -14,7 +14,7 @@
// ...
const loginService = interpret(loginMachine).start();

const loggedInState = await waitFor(loginService, state =>
const loggedInState = await waitFor(loginService, (state) =>
state.hasTag('loggedIn')
);

Expand Down
4 changes: 4 additions & 0 deletions packages/core/actors/package.json
@@ -0,0 +1,4 @@
{
"main": "dist/xstate-actors.cjs.js",
"module": "dist/xstate-actors.esm.js"
}
4 changes: 0 additions & 4 deletions packages/core/behaviors/package.json

This file was deleted.

7 changes: 2 additions & 5 deletions packages/core/package.json
Expand Up @@ -11,10 +11,9 @@
},
"files": [
"dist",
"actor",
"actors",
"actions",
"invoke",
"behaviors",
"guards",
"dev",
"model"
Expand Down Expand Up @@ -51,10 +50,8 @@
"umdName": "XState",
"entrypoints": [
"./index.ts",
"./actor.ts",
"./actions.ts",
"./invoke.ts",
"./behaviors.ts",
"./actors.ts",
"./model.ts",
"./guards.ts",
"./dev/index.ts"
Expand Down
25 changes: 23 additions & 2 deletions packages/core/src/Mailbox.ts
Expand Up @@ -24,6 +24,22 @@ export class Mailbox<T> {
}
}

// TODO: rethink this design
public prepend(event: T): void {
if (!this._current) {
this.enqueue(event);
return;
}

// we know that something is already queued up
// so the mailbox is already flushing or it's inactive
// therefore the only thing that we need to do is to reassign `this._current`
this._current = {
value: event,
next: this._current
};
}

public enqueue(event: T): void {
const enqueued = {
value: event,
Expand All @@ -48,8 +64,13 @@ export class Mailbox<T> {
while (this._current) {
// atm the given _process is responsible for implementing proper try/catch handling
// we assume here that this won't throw in a way that can affect this mailbox
this._process(this._current.value);
this._current = this._current.next;
const consumed = this._current;
this._process(consumed.value);
// something could have been prepended in the meantime
// so we need to be defensive here to avoid skipping over a prepended item
if (consumed === this._current) {
this._current = this._current.next;
}
}
this._last = null;
}
Expand Down

0 comments on commit 7a68cbb

Please sign in to comment.