@xstate/react 4.0.0-alpha.2 → 4.0.0-beta.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +0 -8
- package/dist/declarations/src/createActorContext.d.ts +16 -9
- package/dist/declarations/src/index.d.ts +6 -7
- package/dist/declarations/src/useActor.d.ts +2 -3
- package/dist/declarations/src/useActorRef.d.ts +3 -0
- package/dist/declarations/src/useMachine.d.ts +8 -9
- package/dist/xstate-react.cjs.d.mts +2 -0
- package/dist/xstate-react.cjs.d.mts.map +1 -0
- package/dist/xstate-react.cjs.d.ts +1 -0
- package/dist/xstate-react.cjs.d.ts.map +1 -0
- package/dist/xstate-react.cjs.js +214 -4
- package/dist/xstate-react.cjs.mjs +8 -0
- package/dist/xstate-react.development.cjs.js +220 -0
- package/dist/xstate-react.development.cjs.mjs +8 -0
- package/dist/xstate-react.development.esm.js +189 -0
- package/dist/xstate-react.esm.js +118 -253
- package/package.json +21 -21
- package/dist/declarations/src/fsm.d.ts +0 -3
- package/dist/declarations/src/types.d.ts +0 -15
- package/dist/declarations/src/useConstant.d.ts +0 -1
- package/dist/declarations/src/useInterpret.d.ts +0 -12
- package/dist/declarations/src/useSpawn.d.ts +0 -9
- package/dist/useConstant-0013a606.esm.js +0 -68
- package/dist/useConstant-c09b427a.cjs.prod.js +0 -15
- package/dist/useConstant-ff65b597.cjs.dev.js +0 -71
- package/dist/xstate-react.cjs.dev.js +0 -335
- package/dist/xstate-react.cjs.prod.js +0 -327
- package/fsm/dist/xstate-react-fsm.cjs.d.ts +0 -1
- package/fsm/dist/xstate-react-fsm.cjs.dev.js +0 -87
- package/fsm/dist/xstate-react-fsm.cjs.js +0 -7
- package/fsm/dist/xstate-react-fsm.cjs.prod.js +0 -134
- package/fsm/dist/xstate-react-fsm.esm.js +0 -78
- package/fsm/package.json +0 -4
package/README.md
CHANGED
|
@@ -21,14 +21,6 @@ npm i xstate @xstate/react
|
|
|
21
21
|
|
|
22
22
|
By using the global variable `XStateReact`
|
|
23
23
|
|
|
24
|
-
or
|
|
25
|
-
|
|
26
|
-
```html
|
|
27
|
-
<script src="https://unpkg.com/@xstate/react/dist/xstate-react-fsm.umd.min.js"></script>
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
By using the global variable `XStateReactFSM`
|
|
31
|
-
|
|
32
24
|
2. Import the `useMachine` hook:
|
|
33
25
|
|
|
34
26
|
```js
|
|
@@ -1,15 +1,22 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import { ActorRefFrom, AnyStateMachine,
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
useSelector: <T>(selector: (snapshot: SnapshotFrom<
|
|
6
|
-
useActorRef: () => ActorRefFrom<
|
|
2
|
+
import { ActorRefFrom, AnyStateMachine, SnapshotFrom, ActorOptions, AreAllImplementationsAssumedToBeProvided, MarkAllImplementationsAsProvided, StateMachine, AnyActorLogic } from 'xstate';
|
|
3
|
+
type ToMachinesWithProvidedImplementations<TMachine extends AnyStateMachine> = TMachine extends StateMachine<infer TContext, infer TEvent, infer TActor, infer TAction, infer TGuard, infer TDelay, infer TTag, infer TInput, infer TOutput, infer TResolvedTypesMeta> ? StateMachine<TContext, TEvent, TActor, TAction, TGuard, TDelay, TTag, TInput, TOutput, AreAllImplementationsAssumedToBeProvided<TResolvedTypesMeta> extends false ? MarkAllImplementationsAsProvided<TResolvedTypesMeta> : TResolvedTypesMeta> : never;
|
|
4
|
+
export declare function createActorContext<TLogic extends AnyActorLogic>(actorLogic: TLogic, interpreterOptions?: ActorOptions<TLogic>): {
|
|
5
|
+
useSelector: <T>(selector: (snapshot: SnapshotFrom<TLogic>) => T, compare?: (a: T, b: T) => boolean) => T;
|
|
6
|
+
useActorRef: () => ActorRefFrom<TLogic>;
|
|
7
7
|
Provider: (props: {
|
|
8
8
|
children: React.ReactNode;
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
options?: ActorOptions<TLogic>;
|
|
10
|
+
/**
|
|
11
|
+
* @deprecated Use `logic` instead.
|
|
12
|
+
*/
|
|
13
|
+
machine?: never;
|
|
14
|
+
} & (TLogic extends AnyStateMachine ? AreAllImplementationsAssumedToBeProvided<TLogic['__TResolvedTypesMeta']> extends true ? {
|
|
15
|
+
logic?: TLogic;
|
|
12
16
|
} : {
|
|
13
|
-
|
|
17
|
+
logic: ToMachinesWithProvidedImplementations<TLogic>;
|
|
18
|
+
} : {
|
|
19
|
+
logic?: TLogic;
|
|
14
20
|
})) => React.ReactElement<any, any>;
|
|
15
21
|
};
|
|
22
|
+
export {};
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
export {
|
|
2
|
-
export {
|
|
3
|
-
export {
|
|
4
|
-
export {
|
|
5
|
-
export {
|
|
6
|
-
export {
|
|
7
|
-
export { createActorContext } from './createActorContext.ts';
|
|
1
|
+
export { useActor } from "./useActor.js";
|
|
2
|
+
export { useActorRef } from "./useActorRef.js";
|
|
3
|
+
export { useSelector } from "./useSelector.js";
|
|
4
|
+
export { shallowEqual } from "./shallowEqual.js";
|
|
5
|
+
export { createActorContext } from "./createActorContext.js";
|
|
6
|
+
export { useMachine } from "./useMachine.js";
|
|
@@ -1,3 +1,2 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export declare function useActor<
|
|
3
|
-
export declare function useActor<TEvent extends EventObject, TSnapshot>(actorRef: ActorRef<TEvent, TSnapshot>): [TSnapshot, (event: TEvent) => void];
|
|
1
|
+
import { ActorRefFrom, AnyActorLogic, ActorOptions, SnapshotFrom } from 'xstate';
|
|
2
|
+
export declare function useActor<TLogic extends AnyActorLogic>(logic: TLogic, options?: ActorOptions<TLogic>): [SnapshotFrom<TLogic>, ActorRefFrom<TLogic>['send'], ActorRefFrom<TLogic>];
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { AnyActorLogic, AnyActor, ActorRefFrom, ActorOptions, Observer, SnapshotFrom } from 'xstate';
|
|
2
|
+
export declare function useIdleActor(logic: AnyActorLogic, options: Partial<ActorOptions<AnyActorLogic>>): AnyActor;
|
|
3
|
+
export declare function useActorRef<TLogic extends AnyActorLogic>(machine: TLogic, options?: ActorOptions<TLogic>, observerOrListener?: Observer<SnapshotFrom<TLogic>> | ((value: SnapshotFrom<TLogic>) => void)): ActorRefFrom<TLogic>;
|
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import { AnyStateMachine, AreAllImplementationsAssumedToBeProvided,
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
] : [
|
|
6
|
-
|
|
1
|
+
import { ActorRefFrom, AnyStateMachine, AreAllImplementationsAssumedToBeProvided, ActorOptions, MissingImplementationsError, StateFrom } from 'xstate';
|
|
2
|
+
/**
|
|
3
|
+
* @alias useActor
|
|
4
|
+
*/
|
|
5
|
+
export declare function useMachine<TMachine extends AnyStateMachine>(machine: AreAllImplementationsAssumedToBeProvided<TMachine['__TResolvedTypesMeta']> extends true ? TMachine : MissingImplementationsError<TMachine['__TResolvedTypesMeta']>, options?: ActorOptions<TMachine>): [
|
|
6
|
+
StateFrom<TMachine>,
|
|
7
|
+
ActorRefFrom<TMachine>['send'],
|
|
8
|
+
ActorRefFrom<TMachine>
|
|
7
9
|
];
|
|
8
|
-
type UseMachineReturn<TMachine extends AnyStateMachine, TInterpreter = InterpreterFrom<TMachine>> = [StateFrom<TMachine>, Prop<TInterpreter, 'send'>, TInterpreter];
|
|
9
|
-
export declare function useMachine<TMachine extends AnyStateMachine>(getMachine: MaybeLazy<TMachine>, ...[options]: RestParams<TMachine>): UseMachineReturn<TMachine>;
|
|
10
|
-
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"xstate-react.cjs.d.mts","sourceRoot":"","sources":["./declarations/src/index.d.ts"],"names":[],"mappings":"AAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"xstate-react.cjs.d.ts","sourceRoot":"","sources":["./declarations/src/index.d.ts"],"names":[],"mappings":"AAAA"}
|
package/dist/xstate-react.cjs.js
CHANGED
|
@@ -1,7 +1,217 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
5
|
+
var React = require('react');
|
|
6
|
+
var shim = require('use-sync-external-store/shim');
|
|
7
|
+
var xstate = require('xstate');
|
|
8
|
+
var useIsomorphicLayoutEffect = require('use-isomorphic-layout-effect');
|
|
9
|
+
var withSelector = require('use-sync-external-store/shim/with-selector');
|
|
10
|
+
|
|
11
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { 'default': e }; }
|
|
12
|
+
|
|
13
|
+
function _interopNamespace(e) {
|
|
14
|
+
if (e && e.__esModule) return e;
|
|
15
|
+
var n = Object.create(null);
|
|
16
|
+
if (e) {
|
|
17
|
+
Object.keys(e).forEach(function (k) {
|
|
18
|
+
if (k !== 'default') {
|
|
19
|
+
var d = Object.getOwnPropertyDescriptor(e, k);
|
|
20
|
+
Object.defineProperty(n, k, d.get ? d : {
|
|
21
|
+
enumerable: true,
|
|
22
|
+
get: function () { return e[k]; }
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
n["default"] = e;
|
|
28
|
+
return Object.freeze(n);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
var React__namespace = /*#__PURE__*/_interopNamespace(React);
|
|
32
|
+
var useIsomorphicLayoutEffect__default = /*#__PURE__*/_interopDefault(useIsomorphicLayoutEffect);
|
|
33
|
+
|
|
34
|
+
const forEachActor = (actorRef, callback) => {
|
|
35
|
+
callback(actorRef);
|
|
36
|
+
const children = actorRef.getSnapshot().children;
|
|
37
|
+
if (children) {
|
|
38
|
+
Object.values(children).forEach(child => {
|
|
39
|
+
forEachActor(child, callback);
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
function stopRootWithRehydration(actorRef) {
|
|
44
|
+
// persist state here in a custom way allows us to persist inline actors and to preserve actor references
|
|
45
|
+
// we do it to avoid setState in useEffect when the effect gets "reconnected"
|
|
46
|
+
// this currently only happens in Strict Effects but it simulates the Offscreen aka Activity API
|
|
47
|
+
// it also just allows us to end up with a somewhat more predictable behavior for the users
|
|
48
|
+
const persistedSnapshots = [];
|
|
49
|
+
forEachActor(actorRef, ref => {
|
|
50
|
+
persistedSnapshots.push([ref, ref.getSnapshot()]);
|
|
51
|
+
// muting observers allow us to avoid `useSelector` from being notified about the stopped state
|
|
52
|
+
// React reconnects its subscribers (from the useSyncExternalStore) on its own
|
|
53
|
+
// and userland subscibers should basically always do the same anyway
|
|
54
|
+
// as each subscription should have its own cleanup logic and that should be called each such reconnect
|
|
55
|
+
ref.observers = new Set();
|
|
56
|
+
});
|
|
57
|
+
actorRef.stop();
|
|
58
|
+
persistedSnapshots.forEach(([ref, snapshot]) => {
|
|
59
|
+
ref._processingStatus = 0;
|
|
60
|
+
ref._state = snapshot;
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function useIdleActor(logic, options) {
|
|
65
|
+
let [[currentConfig, actorRef], setCurrent] = React.useState(() => {
|
|
66
|
+
const actorRef = xstate.createActor(logic, options);
|
|
67
|
+
return [logic.config, actorRef];
|
|
68
|
+
});
|
|
69
|
+
if (logic.config !== currentConfig) {
|
|
70
|
+
const newActorRef = xstate.createActor(logic, {
|
|
71
|
+
...options,
|
|
72
|
+
state: actorRef.getPersistedState({
|
|
73
|
+
__unsafeAllowInlineActors: true
|
|
74
|
+
})
|
|
75
|
+
});
|
|
76
|
+
setCurrent([logic.config, newActorRef]);
|
|
77
|
+
actorRef = newActorRef;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// TODO: consider using `useAsapEffect` that would do this in `useInsertionEffect` is that's available
|
|
81
|
+
useIsomorphicLayoutEffect__default["default"](() => {
|
|
82
|
+
actorRef.logic.implementations = logic.implementations;
|
|
83
|
+
});
|
|
84
|
+
return actorRef;
|
|
85
|
+
}
|
|
86
|
+
function useActorRef(machine, options = {}, observerOrListener) {
|
|
87
|
+
const actorRef = useIdleActor(machine, options);
|
|
88
|
+
React.useEffect(() => {
|
|
89
|
+
if (!observerOrListener) {
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
let sub = actorRef.subscribe(xstate.toObserver(observerOrListener));
|
|
93
|
+
return () => {
|
|
94
|
+
sub.unsubscribe();
|
|
95
|
+
};
|
|
96
|
+
}, [observerOrListener]);
|
|
97
|
+
React.useEffect(() => {
|
|
98
|
+
actorRef.start();
|
|
99
|
+
return () => {
|
|
100
|
+
stopRootWithRehydration(actorRef);
|
|
101
|
+
};
|
|
102
|
+
}, [actorRef]);
|
|
103
|
+
return actorRef;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function useActor(logic, options = {}) {
|
|
107
|
+
const actorRef = useIdleActor(logic, options);
|
|
108
|
+
const getSnapshot = React.useCallback(() => {
|
|
109
|
+
return actorRef.getSnapshot();
|
|
110
|
+
}, [actorRef]);
|
|
111
|
+
const subscribe = React.useCallback(handleStoreChange => {
|
|
112
|
+
const {
|
|
113
|
+
unsubscribe
|
|
114
|
+
} = actorRef.subscribe(handleStoreChange);
|
|
115
|
+
return unsubscribe;
|
|
116
|
+
}, [actorRef]);
|
|
117
|
+
const actorSnapshot = shim.useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
|
|
118
|
+
React.useEffect(() => {
|
|
119
|
+
actorRef.start();
|
|
120
|
+
return () => {
|
|
121
|
+
stopRootWithRehydration(actorRef);
|
|
122
|
+
};
|
|
123
|
+
}, [actorRef]);
|
|
124
|
+
return [actorSnapshot, actorRef.send, actorRef];
|
|
7
125
|
}
|
|
126
|
+
|
|
127
|
+
function defaultCompare(a, b) {
|
|
128
|
+
return a === b;
|
|
129
|
+
}
|
|
130
|
+
function useSelector(actor, selector, compare = defaultCompare) {
|
|
131
|
+
const subscribe = React.useCallback(handleStoreChange => {
|
|
132
|
+
const {
|
|
133
|
+
unsubscribe
|
|
134
|
+
} = actor.subscribe(handleStoreChange);
|
|
135
|
+
return unsubscribe;
|
|
136
|
+
}, [actor]);
|
|
137
|
+
const boundGetSnapshot = React.useCallback(() => actor.getSnapshot(), [actor]);
|
|
138
|
+
const selectedSnapshot = withSelector.useSyncExternalStoreWithSelector(subscribe, boundGetSnapshot, boundGetSnapshot, selector, compare);
|
|
139
|
+
return selectedSnapshot;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// From https://github.com/reduxjs/react-redux/blob/720f0ba79236cdc3e1115f4ef9a7760a21784b48/src/utils/shallowEqual.ts
|
|
143
|
+
function is(x, y) {
|
|
144
|
+
if (x === y) {
|
|
145
|
+
return x !== 0 || y !== 0 || 1 / x === 1 / y;
|
|
146
|
+
} else {
|
|
147
|
+
return x !== x && y !== y;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
function shallowEqual(objA, objB) {
|
|
151
|
+
if (is(objA, objB)) return true;
|
|
152
|
+
if (typeof objA !== 'object' || objA === null || typeof objB !== 'object' || objB === null) {
|
|
153
|
+
return false;
|
|
154
|
+
}
|
|
155
|
+
const keysA = Object.keys(objA);
|
|
156
|
+
const keysB = Object.keys(objB);
|
|
157
|
+
if (keysA.length !== keysB.length) return false;
|
|
158
|
+
for (let i = 0; i < keysA.length; i++) {
|
|
159
|
+
if (!Object.prototype.hasOwnProperty.call(objB, keysA[i]) || !is(objA[keysA[i]], objB[keysA[i]])) {
|
|
160
|
+
return false;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
return true;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function createActorContext(actorLogic, interpreterOptions) {
|
|
167
|
+
const ReactContext = /*#__PURE__*/React__namespace.createContext(null);
|
|
168
|
+
const OriginalProvider = ReactContext.Provider;
|
|
169
|
+
function Provider({
|
|
170
|
+
children,
|
|
171
|
+
logic: providedLogic = actorLogic,
|
|
172
|
+
machine,
|
|
173
|
+
options: providedOptions = interpreterOptions
|
|
174
|
+
}) {
|
|
175
|
+
if (machine) {
|
|
176
|
+
throw new Error(`The "machine" prop has been deprecated. Please use "logic" instead.`);
|
|
177
|
+
}
|
|
178
|
+
const actor = useActorRef(providedLogic, providedOptions);
|
|
179
|
+
return /*#__PURE__*/React__namespace.createElement(OriginalProvider, {
|
|
180
|
+
value: actor,
|
|
181
|
+
children
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// TODO: add properties to actor ref to make more descriptive
|
|
186
|
+
Provider.displayName = `ActorProvider`;
|
|
187
|
+
function useContext() {
|
|
188
|
+
const actor = React__namespace.useContext(ReactContext);
|
|
189
|
+
if (!actor) {
|
|
190
|
+
throw new Error(`You used a hook from "${Provider.displayName}" but it's not inside a <${Provider.displayName}> component.`);
|
|
191
|
+
}
|
|
192
|
+
return actor;
|
|
193
|
+
}
|
|
194
|
+
function useSelector$1(selector, compare) {
|
|
195
|
+
const actor = useContext();
|
|
196
|
+
return useSelector(actor, selector, compare);
|
|
197
|
+
}
|
|
198
|
+
return {
|
|
199
|
+
Provider: Provider,
|
|
200
|
+
useActorRef: useContext,
|
|
201
|
+
useSelector: useSelector$1
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* @alias useActor
|
|
207
|
+
*/
|
|
208
|
+
function useMachine(machine, options = {}) {
|
|
209
|
+
return useActor(machine, options);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
exports.createActorContext = createActorContext;
|
|
213
|
+
exports.shallowEqual = shallowEqual;
|
|
214
|
+
exports.useActor = useActor;
|
|
215
|
+
exports.useActorRef = useActorRef;
|
|
216
|
+
exports.useMachine = useMachine;
|
|
217
|
+
exports.useSelector = useSelector;
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
5
|
+
var React = require('react');
|
|
6
|
+
var shim = require('use-sync-external-store/shim');
|
|
7
|
+
var xstate = require('xstate');
|
|
8
|
+
var useIsomorphicLayoutEffect = require('use-isomorphic-layout-effect');
|
|
9
|
+
var withSelector = require('use-sync-external-store/shim/with-selector');
|
|
10
|
+
|
|
11
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { 'default': e }; }
|
|
12
|
+
|
|
13
|
+
function _interopNamespace(e) {
|
|
14
|
+
if (e && e.__esModule) return e;
|
|
15
|
+
var n = Object.create(null);
|
|
16
|
+
if (e) {
|
|
17
|
+
Object.keys(e).forEach(function (k) {
|
|
18
|
+
if (k !== 'default') {
|
|
19
|
+
var d = Object.getOwnPropertyDescriptor(e, k);
|
|
20
|
+
Object.defineProperty(n, k, d.get ? d : {
|
|
21
|
+
enumerable: true,
|
|
22
|
+
get: function () { return e[k]; }
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
n["default"] = e;
|
|
28
|
+
return Object.freeze(n);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
var React__namespace = /*#__PURE__*/_interopNamespace(React);
|
|
32
|
+
var useIsomorphicLayoutEffect__default = /*#__PURE__*/_interopDefault(useIsomorphicLayoutEffect);
|
|
33
|
+
|
|
34
|
+
const forEachActor = (actorRef, callback) => {
|
|
35
|
+
callback(actorRef);
|
|
36
|
+
const children = actorRef.getSnapshot().children;
|
|
37
|
+
if (children) {
|
|
38
|
+
Object.values(children).forEach(child => {
|
|
39
|
+
forEachActor(child, callback);
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
function stopRootWithRehydration(actorRef) {
|
|
44
|
+
// persist state here in a custom way allows us to persist inline actors and to preserve actor references
|
|
45
|
+
// we do it to avoid setState in useEffect when the effect gets "reconnected"
|
|
46
|
+
// this currently only happens in Strict Effects but it simulates the Offscreen aka Activity API
|
|
47
|
+
// it also just allows us to end up with a somewhat more predictable behavior for the users
|
|
48
|
+
const persistedSnapshots = [];
|
|
49
|
+
forEachActor(actorRef, ref => {
|
|
50
|
+
persistedSnapshots.push([ref, ref.getSnapshot()]);
|
|
51
|
+
// muting observers allow us to avoid `useSelector` from being notified about the stopped state
|
|
52
|
+
// React reconnects its subscribers (from the useSyncExternalStore) on its own
|
|
53
|
+
// and userland subscibers should basically always do the same anyway
|
|
54
|
+
// as each subscription should have its own cleanup logic and that should be called each such reconnect
|
|
55
|
+
ref.observers = new Set();
|
|
56
|
+
});
|
|
57
|
+
actorRef.stop();
|
|
58
|
+
persistedSnapshots.forEach(([ref, snapshot]) => {
|
|
59
|
+
ref._processingStatus = 0;
|
|
60
|
+
ref._state = snapshot;
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function useIdleActor(logic, options) {
|
|
65
|
+
let [[currentConfig, actorRef], setCurrent] = React.useState(() => {
|
|
66
|
+
const actorRef = xstate.createActor(logic, options);
|
|
67
|
+
return [logic.config, actorRef];
|
|
68
|
+
});
|
|
69
|
+
if (logic.config !== currentConfig) {
|
|
70
|
+
const newActorRef = xstate.createActor(logic, {
|
|
71
|
+
...options,
|
|
72
|
+
state: actorRef.getPersistedState({
|
|
73
|
+
__unsafeAllowInlineActors: true
|
|
74
|
+
})
|
|
75
|
+
});
|
|
76
|
+
setCurrent([logic.config, newActorRef]);
|
|
77
|
+
actorRef = newActorRef;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// TODO: consider using `useAsapEffect` that would do this in `useInsertionEffect` is that's available
|
|
81
|
+
useIsomorphicLayoutEffect__default["default"](() => {
|
|
82
|
+
actorRef.logic.implementations = logic.implementations;
|
|
83
|
+
});
|
|
84
|
+
return actorRef;
|
|
85
|
+
}
|
|
86
|
+
function useActorRef(machine, options = {}, observerOrListener) {
|
|
87
|
+
const actorRef = useIdleActor(machine, options);
|
|
88
|
+
React.useEffect(() => {
|
|
89
|
+
if (!observerOrListener) {
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
let sub = actorRef.subscribe(xstate.toObserver(observerOrListener));
|
|
93
|
+
return () => {
|
|
94
|
+
sub.unsubscribe();
|
|
95
|
+
};
|
|
96
|
+
}, [observerOrListener]);
|
|
97
|
+
React.useEffect(() => {
|
|
98
|
+
actorRef.start();
|
|
99
|
+
return () => {
|
|
100
|
+
stopRootWithRehydration(actorRef);
|
|
101
|
+
};
|
|
102
|
+
}, [actorRef]);
|
|
103
|
+
return actorRef;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function useActor(logic, options = {}) {
|
|
107
|
+
if (!!logic && 'send' in logic && typeof logic.send === 'function') {
|
|
108
|
+
throw new Error(`useActor() expects actor logic (e.g. a machine), but received an ActorRef. Use the useSelector(actorRef, ...) hook instead to read the ActorRef's snapshot.`);
|
|
109
|
+
}
|
|
110
|
+
const actorRef = useIdleActor(logic, options);
|
|
111
|
+
const getSnapshot = React.useCallback(() => {
|
|
112
|
+
return actorRef.getSnapshot();
|
|
113
|
+
}, [actorRef]);
|
|
114
|
+
const subscribe = React.useCallback(handleStoreChange => {
|
|
115
|
+
const {
|
|
116
|
+
unsubscribe
|
|
117
|
+
} = actorRef.subscribe(handleStoreChange);
|
|
118
|
+
return unsubscribe;
|
|
119
|
+
}, [actorRef]);
|
|
120
|
+
const actorSnapshot = shim.useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
|
|
121
|
+
React.useEffect(() => {
|
|
122
|
+
actorRef.start();
|
|
123
|
+
return () => {
|
|
124
|
+
stopRootWithRehydration(actorRef);
|
|
125
|
+
};
|
|
126
|
+
}, [actorRef]);
|
|
127
|
+
return [actorSnapshot, actorRef.send, actorRef];
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function defaultCompare(a, b) {
|
|
131
|
+
return a === b;
|
|
132
|
+
}
|
|
133
|
+
function useSelector(actor, selector, compare = defaultCompare) {
|
|
134
|
+
const subscribe = React.useCallback(handleStoreChange => {
|
|
135
|
+
const {
|
|
136
|
+
unsubscribe
|
|
137
|
+
} = actor.subscribe(handleStoreChange);
|
|
138
|
+
return unsubscribe;
|
|
139
|
+
}, [actor]);
|
|
140
|
+
const boundGetSnapshot = React.useCallback(() => actor.getSnapshot(), [actor]);
|
|
141
|
+
const selectedSnapshot = withSelector.useSyncExternalStoreWithSelector(subscribe, boundGetSnapshot, boundGetSnapshot, selector, compare);
|
|
142
|
+
return selectedSnapshot;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// From https://github.com/reduxjs/react-redux/blob/720f0ba79236cdc3e1115f4ef9a7760a21784b48/src/utils/shallowEqual.ts
|
|
146
|
+
function is(x, y) {
|
|
147
|
+
if (x === y) {
|
|
148
|
+
return x !== 0 || y !== 0 || 1 / x === 1 / y;
|
|
149
|
+
} else {
|
|
150
|
+
return x !== x && y !== y;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
function shallowEqual(objA, objB) {
|
|
154
|
+
if (is(objA, objB)) return true;
|
|
155
|
+
if (typeof objA !== 'object' || objA === null || typeof objB !== 'object' || objB === null) {
|
|
156
|
+
return false;
|
|
157
|
+
}
|
|
158
|
+
const keysA = Object.keys(objA);
|
|
159
|
+
const keysB = Object.keys(objB);
|
|
160
|
+
if (keysA.length !== keysB.length) return false;
|
|
161
|
+
for (let i = 0; i < keysA.length; i++) {
|
|
162
|
+
if (!Object.prototype.hasOwnProperty.call(objB, keysA[i]) || !is(objA[keysA[i]], objB[keysA[i]])) {
|
|
163
|
+
return false;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
return true;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function createActorContext(actorLogic, interpreterOptions) {
|
|
170
|
+
const ReactContext = /*#__PURE__*/React__namespace.createContext(null);
|
|
171
|
+
const OriginalProvider = ReactContext.Provider;
|
|
172
|
+
function Provider({
|
|
173
|
+
children,
|
|
174
|
+
logic: providedLogic = actorLogic,
|
|
175
|
+
machine,
|
|
176
|
+
options: providedOptions = interpreterOptions
|
|
177
|
+
}) {
|
|
178
|
+
if (machine) {
|
|
179
|
+
throw new Error(`The "machine" prop has been deprecated. Please use "logic" instead.`);
|
|
180
|
+
}
|
|
181
|
+
const actor = useActorRef(providedLogic, providedOptions);
|
|
182
|
+
return /*#__PURE__*/React__namespace.createElement(OriginalProvider, {
|
|
183
|
+
value: actor,
|
|
184
|
+
children
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// TODO: add properties to actor ref to make more descriptive
|
|
189
|
+
Provider.displayName = `ActorProvider`;
|
|
190
|
+
function useContext() {
|
|
191
|
+
const actor = React__namespace.useContext(ReactContext);
|
|
192
|
+
if (!actor) {
|
|
193
|
+
throw new Error(`You used a hook from "${Provider.displayName}" but it's not inside a <${Provider.displayName}> component.`);
|
|
194
|
+
}
|
|
195
|
+
return actor;
|
|
196
|
+
}
|
|
197
|
+
function useSelector$1(selector, compare) {
|
|
198
|
+
const actor = useContext();
|
|
199
|
+
return useSelector(actor, selector, compare);
|
|
200
|
+
}
|
|
201
|
+
return {
|
|
202
|
+
Provider: Provider,
|
|
203
|
+
useActorRef: useContext,
|
|
204
|
+
useSelector: useSelector$1
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* @alias useActor
|
|
210
|
+
*/
|
|
211
|
+
function useMachine(machine, options = {}) {
|
|
212
|
+
return useActor(machine, options);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
exports.createActorContext = createActorContext;
|
|
216
|
+
exports.shallowEqual = shallowEqual;
|
|
217
|
+
exports.useActor = useActor;
|
|
218
|
+
exports.useActorRef = useActorRef;
|
|
219
|
+
exports.useMachine = useMachine;
|
|
220
|
+
exports.useSelector = useSelector;
|