@xstate/react 7.0.0-alpha.7 → 7.0.0-beta.0
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 +1 -1
- package/dist/declarations/src/createActorContext.d.ts +5 -0
- package/dist/declarations/src/useActor.d.ts +8 -1
- package/dist/declarations/src/useActorRef.d.ts +14 -2
- package/dist/declarations/src/useMachine.d.ts +1 -0
- package/dist/xstate-react.cjs.js +45 -12
- package/dist/xstate-react.development.cjs.js +47 -14
- package/dist/xstate-react.development.esm.js +48 -15
- package/dist/xstate-react.esm.js +46 -13
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -8,5 +8,10 @@ export declare function createActorContext<TLogic extends AnyActorLogic>(actorLo
|
|
|
8
8
|
children: React.ReactNode;
|
|
9
9
|
options?: ActorOptions<TLogic>;
|
|
10
10
|
logic?: TLogic;
|
|
11
|
+
/**
|
|
12
|
+
* An already-created actor (e.g. from `restoreActor`) to provide instead
|
|
13
|
+
* of creating one from `logic` and `options`. Create it once.
|
|
14
|
+
*/
|
|
15
|
+
actor?: Actor<TLogic>;
|
|
11
16
|
}) => React.ReactElement<any, any>;
|
|
12
17
|
};
|
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
import { Actor, ActorOptions, AnyActorLogic, SnapshotFrom, type ConditionalRequired, type IsNotNever, type RequiredActorOptionsKeys, type RequiredActorOptionsFor } from 'xstate';
|
|
2
|
-
/**
|
|
2
|
+
/**
|
|
3
|
+
* Creates an actor from `logic` (or uses an already-created `actor`, e.g. from
|
|
4
|
+
* `restoreActor`) and subscribes to its snapshot. Create a passed actor once,
|
|
5
|
+
* e.g. `useState(() => restoreActor(machine, persisted))[0]`.
|
|
6
|
+
*
|
|
7
|
+
* @public
|
|
8
|
+
*/
|
|
9
|
+
export declare function useActor<TLogic extends AnyActorLogic>(actor: Actor<TLogic>): [SnapshotFrom<TLogic>, Actor<TLogic>['send'], Actor<TLogic>];
|
|
3
10
|
export declare function useActor<TLogic extends AnyActorLogic>(logic: TLogic, ...[options]: ConditionalRequired<[
|
|
4
11
|
options?: ActorOptions<TLogic> & RequiredActorOptionsFor<TLogic>
|
|
5
12
|
], IsNotNever<RequiredActorOptionsKeys<TLogic>>>): [SnapshotFrom<TLogic>, Actor<TLogic>['send'], Actor<TLogic>];
|
|
@@ -1,9 +1,21 @@
|
|
|
1
1
|
import { Actor, ActorOptions, AnyActorLogic, Observer, SnapshotFrom, type ConditionalRequired, type IsNotNever, type RequiredActorOptionsKeys, type RequiredActorOptionsFor } from 'xstate';
|
|
2
|
-
|
|
2
|
+
/**
|
|
3
|
+
* An already-created actor (e.g. from `restoreActor`). Hooks use it as is
|
|
4
|
+
* instead of creating one from logic.
|
|
5
|
+
*/
|
|
6
|
+
export declare function isActor(value: unknown): value is Actor<AnyActorLogic>;
|
|
7
|
+
export declare function useIdleActorRef<TLogic extends AnyActorLogic>(logicOrActor: TLogic | Actor<TLogic>, ...[options]: ConditionalRequired<[
|
|
3
8
|
options?: ActorOptions<TLogic> & RequiredActorOptionsFor<TLogic>
|
|
4
9
|
], IsNotNever<RequiredActorOptionsKeys<TLogic>>>): [Actor<TLogic>, (actorRef: Actor<TLogic>) => void];
|
|
5
10
|
export declare function useActorLifecycle<TLogic extends AnyActorLogic>(actorRef: Actor<TLogic>, setActorRef: (actorRef: Actor<TLogic>) => void, createReplacement: () => Actor<TLogic>): void;
|
|
6
|
-
/**
|
|
11
|
+
/**
|
|
12
|
+
* Creates an actor from `logic` (or uses an already-created `actor`, e.g. from
|
|
13
|
+
* `restoreActor`), starts it on mount and stops it on unmount. Create a passed
|
|
14
|
+
* actor once, e.g. `useState(() => restoreActor(machine, persisted))[0]`.
|
|
15
|
+
*
|
|
16
|
+
* @public
|
|
17
|
+
*/
|
|
18
|
+
export declare function useActorRef<TLogic extends AnyActorLogic>(actor: Actor<TLogic>, observerOrListener?: Observer<SnapshotFrom<TLogic>> | ((value: SnapshotFrom<TLogic>) => void)): Actor<TLogic>;
|
|
7
19
|
export declare function useActorRef<TLogic extends AnyActorLogic>(machine: TLogic, ...[options, observerOrListener]: IsNotNever<RequiredActorOptionsKeys<TLogic>> extends true ? [
|
|
8
20
|
options: ActorOptions<TLogic> & RequiredActorOptionsFor<TLogic>,
|
|
9
21
|
observerOrListener?: Observer<SnapshotFrom<TLogic>> | ((value: SnapshotFrom<TLogic>) => void)
|
|
@@ -5,6 +5,7 @@ import { Actor, ActorOptions, AnyStateMachine, StateFrom, type ConditionalRequir
|
|
|
5
5
|
* @deprecated Use `useActor(machine, options)`.
|
|
6
6
|
* @public
|
|
7
7
|
*/
|
|
8
|
+
export declare function useMachine<TMachine extends AnyStateMachine>(actor: Actor<TMachine>): [StateFrom<TMachine>, Actor<TMachine>['send'], Actor<TMachine>];
|
|
8
9
|
export declare function useMachine<TMachine extends AnyStateMachine>(machine: TMachine, ...[options]: ConditionalRequired<[
|
|
9
10
|
options?: ActorOptions<TMachine> & RequiredActorOptionsFor<TMachine>
|
|
10
11
|
], IsNotNever<RequiredActorOptionsKeys<TMachine>>>): [StateFrom<TMachine>, Actor<TMachine>['send'], Actor<TMachine>];
|
package/dist/xstate-react.cjs.js
CHANGED
|
@@ -35,15 +35,23 @@ function isStateMachine(logic) {
|
|
|
35
35
|
return typeof logic.provide === 'function' && !!logic.config && !!logic.sources;
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
+
/**
|
|
39
|
+
* An already-created actor (e.g. from `restoreActor`). Hooks use it as is
|
|
40
|
+
* instead of creating one from logic.
|
|
41
|
+
*/
|
|
42
|
+
function isActor(value) {
|
|
43
|
+
return value instanceof xstate.Actor;
|
|
44
|
+
}
|
|
45
|
+
|
|
38
46
|
// The hook updates the sources of its machine on later renders. A per-hook
|
|
39
47
|
// copy keeps those updates from reaching the caller's machine, which may be
|
|
40
48
|
// shared by other actors.
|
|
41
49
|
function ownLogic(logic) {
|
|
42
50
|
return isStateMachine(logic) ? logic.provide({}) : logic;
|
|
43
51
|
}
|
|
44
|
-
function useIdleActorRef(
|
|
52
|
+
function useIdleActorRef(logicOrActor, ...[options]) {
|
|
45
53
|
const [actorRef, setActorRef] = React.useState(() => {
|
|
46
|
-
return xstate.createActor(ownLogic(
|
|
54
|
+
return isActor(logicOrActor) ? logicOrActor : xstate.createActor(ownLogic(logicOrActor), options);
|
|
47
55
|
});
|
|
48
56
|
// An object whose identity changes only when React Fast Refresh re-renders
|
|
49
57
|
// this component. Fast Refresh ignores dependency lists while it applies an
|
|
@@ -57,6 +65,11 @@ function useIdleActorRef(logic, ...[options]) {
|
|
|
57
65
|
// `machine.provide()` for the same machine config.
|
|
58
66
|
// TODO: consider using `useAsapEffect` that would do this in `useInsertionEffect` is that's available
|
|
59
67
|
useIsomorphicLayoutEffect__default["default"](() => {
|
|
68
|
+
// A ready actor is owned as is; its logic is not updated by later renders.
|
|
69
|
+
if (isActor(logicOrActor)) {
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
const logic = logicOrActor;
|
|
60
73
|
const currentLogic = actorRef.logic;
|
|
61
74
|
refreshSignalRef.current !== refreshSignal;
|
|
62
75
|
refreshSignalRef.current = refreshSignal;
|
|
@@ -104,9 +117,19 @@ function useActorLifecycle(actorRef, setActorRef, createReplacement) {
|
|
|
104
117
|
}, [actorRef]);
|
|
105
118
|
}
|
|
106
119
|
|
|
107
|
-
/**
|
|
108
|
-
|
|
109
|
-
|
|
120
|
+
/**
|
|
121
|
+
* Creates an actor from `logic` (or uses an already-created `actor`, e.g. from
|
|
122
|
+
* `restoreActor`), starts it on mount and stops it on unmount. Create a passed
|
|
123
|
+
* actor once, e.g. `useState(() => restoreActor(machine, persisted))[0]`.
|
|
124
|
+
*
|
|
125
|
+
* @public
|
|
126
|
+
*/
|
|
127
|
+
|
|
128
|
+
function useActorRef(logicOrActor, ...args) {
|
|
129
|
+
const readyActor = isActor(logicOrActor);
|
|
130
|
+
const options = readyActor ? undefined : args[0];
|
|
131
|
+
const observerOrListener = readyActor ? args[0] : args[1];
|
|
132
|
+
const [actorRef, setActorRef] = useIdleActorRef(logicOrActor, options);
|
|
110
133
|
React.useEffect(() => {
|
|
111
134
|
if (!observerOrListener) {
|
|
112
135
|
return;
|
|
@@ -156,9 +179,10 @@ function createActorContext(actorLogic, actorOptions) {
|
|
|
156
179
|
function Provider({
|
|
157
180
|
children,
|
|
158
181
|
logic: providedLogic = actorLogic,
|
|
159
|
-
options: providedOptions
|
|
182
|
+
options: providedOptions,
|
|
183
|
+
actor: providedActor
|
|
160
184
|
}) {
|
|
161
|
-
const actor = useActorRef(providedLogic, {
|
|
185
|
+
const actor = useActorRef(providedActor ?? providedLogic, providedActor ? undefined : {
|
|
162
186
|
...actorOptions,
|
|
163
187
|
...providedOptions
|
|
164
188
|
});
|
|
@@ -214,9 +238,17 @@ function shallowEqual(objA, objB) {
|
|
|
214
238
|
return true;
|
|
215
239
|
}
|
|
216
240
|
|
|
217
|
-
/**
|
|
218
|
-
|
|
219
|
-
|
|
241
|
+
/**
|
|
242
|
+
* Creates an actor from `logic` (or uses an already-created `actor`, e.g. from
|
|
243
|
+
* `restoreActor`) and subscribes to its snapshot. Create a passed actor once,
|
|
244
|
+
* e.g. `useState(() => restoreActor(machine, persisted))[0]`.
|
|
245
|
+
*
|
|
246
|
+
* @public
|
|
247
|
+
*/
|
|
248
|
+
|
|
249
|
+
function useActor(logic, options) {
|
|
250
|
+
const readyActor = isActor(logic);
|
|
251
|
+
const [actorRef, setActorRef] = useIdleActorRef(logic, ...[options]);
|
|
220
252
|
const getSnapshot = React.useCallback(() => {
|
|
221
253
|
return actorRef.getSnapshot();
|
|
222
254
|
}, [actorRef]);
|
|
@@ -234,7 +266,7 @@ function useActor(logic, ...[options]) {
|
|
|
234
266
|
if (snapshotWithStatus?.status === 'error') {
|
|
235
267
|
throw snapshotWithStatus.error;
|
|
236
268
|
}
|
|
237
|
-
useActorLifecycle(actorRef, setActorRef, () => xstate.createActor(actorRef.logic, options));
|
|
269
|
+
useActorLifecycle(actorRef, setActorRef, () => xstate.createActor(actorRef.logic, readyActor ? actorRef.options : options));
|
|
238
270
|
return [actorSnapshot, actorRef.send, actorRef];
|
|
239
271
|
}
|
|
240
272
|
|
|
@@ -244,7 +276,8 @@ function useActor(logic, ...[options]) {
|
|
|
244
276
|
* @deprecated Use `useActor(machine, options)`.
|
|
245
277
|
* @public
|
|
246
278
|
*/
|
|
247
|
-
|
|
279
|
+
|
|
280
|
+
function useMachine(machine, options) {
|
|
248
281
|
return useActor(machine, options);
|
|
249
282
|
}
|
|
250
283
|
|
|
@@ -35,15 +35,23 @@ function isStateMachine(logic) {
|
|
|
35
35
|
return typeof logic.provide === 'function' && !!logic.config && !!logic.sources;
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
+
/**
|
|
39
|
+
* An already-created actor (e.g. from `restoreActor`). Hooks use it as is
|
|
40
|
+
* instead of creating one from logic.
|
|
41
|
+
*/
|
|
42
|
+
function isActor(value) {
|
|
43
|
+
return value instanceof xstate.Actor;
|
|
44
|
+
}
|
|
45
|
+
|
|
38
46
|
// The hook updates the sources of its machine on later renders. A per-hook
|
|
39
47
|
// copy keeps those updates from reaching the caller's machine, which may be
|
|
40
48
|
// shared by other actors.
|
|
41
49
|
function ownLogic(logic) {
|
|
42
50
|
return isStateMachine(logic) ? logic.provide({}) : logic;
|
|
43
51
|
}
|
|
44
|
-
function useIdleActorRef(
|
|
52
|
+
function useIdleActorRef(logicOrActor, ...[options]) {
|
|
45
53
|
const [actorRef, setActorRef] = React.useState(() => {
|
|
46
|
-
return xstate.createActor(ownLogic(
|
|
54
|
+
return isActor(logicOrActor) ? logicOrActor : xstate.createActor(ownLogic(logicOrActor), options);
|
|
47
55
|
});
|
|
48
56
|
// An object whose identity changes only when React Fast Refresh re-renders
|
|
49
57
|
// this component. Fast Refresh ignores dependency lists while it applies an
|
|
@@ -57,6 +65,11 @@ function useIdleActorRef(logic, ...[options]) {
|
|
|
57
65
|
// `machine.provide()` for the same machine config.
|
|
58
66
|
// TODO: consider using `useAsapEffect` that would do this in `useInsertionEffect` is that's available
|
|
59
67
|
useIsomorphicLayoutEffect__default["default"](() => {
|
|
68
|
+
// A ready actor is owned as is; its logic is not updated by later renders.
|
|
69
|
+
if (isActor(logicOrActor)) {
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
const logic = logicOrActor;
|
|
60
73
|
const currentLogic = actorRef.logic;
|
|
61
74
|
const refreshed = refreshSignalRef.current !== refreshSignal;
|
|
62
75
|
refreshSignalRef.current = refreshSignal;
|
|
@@ -114,9 +127,19 @@ function useActorLifecycle(actorRef, setActorRef, createReplacement) {
|
|
|
114
127
|
}, [actorRef]);
|
|
115
128
|
}
|
|
116
129
|
|
|
117
|
-
/**
|
|
118
|
-
|
|
119
|
-
|
|
130
|
+
/**
|
|
131
|
+
* Creates an actor from `logic` (or uses an already-created `actor`, e.g. from
|
|
132
|
+
* `restoreActor`), starts it on mount and stops it on unmount. Create a passed
|
|
133
|
+
* actor once, e.g. `useState(() => restoreActor(machine, persisted))[0]`.
|
|
134
|
+
*
|
|
135
|
+
* @public
|
|
136
|
+
*/
|
|
137
|
+
|
|
138
|
+
function useActorRef(logicOrActor, ...args) {
|
|
139
|
+
const readyActor = isActor(logicOrActor);
|
|
140
|
+
const options = readyActor ? undefined : args[0];
|
|
141
|
+
const observerOrListener = readyActor ? args[0] : args[1];
|
|
142
|
+
const [actorRef, setActorRef] = useIdleActorRef(logicOrActor, options);
|
|
120
143
|
React.useEffect(() => {
|
|
121
144
|
if (!observerOrListener) {
|
|
122
145
|
return;
|
|
@@ -166,9 +189,10 @@ function createActorContext(actorLogic, actorOptions) {
|
|
|
166
189
|
function Provider({
|
|
167
190
|
children,
|
|
168
191
|
logic: providedLogic = actorLogic,
|
|
169
|
-
options: providedOptions
|
|
192
|
+
options: providedOptions,
|
|
193
|
+
actor: providedActor
|
|
170
194
|
}) {
|
|
171
|
-
const actor = useActorRef(providedLogic, {
|
|
195
|
+
const actor = useActorRef(providedActor ?? providedLogic, providedActor ? undefined : {
|
|
172
196
|
...actorOptions,
|
|
173
197
|
...providedOptions
|
|
174
198
|
});
|
|
@@ -224,12 +248,20 @@ function shallowEqual(objA, objB) {
|
|
|
224
248
|
return true;
|
|
225
249
|
}
|
|
226
250
|
|
|
227
|
-
/**
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
251
|
+
/**
|
|
252
|
+
* Creates an actor from `logic` (or uses an already-created `actor`, e.g. from
|
|
253
|
+
* `restoreActor`) and subscribes to its snapshot. Create a passed actor once,
|
|
254
|
+
* e.g. `useState(() => restoreActor(machine, persisted))[0]`.
|
|
255
|
+
*
|
|
256
|
+
* @public
|
|
257
|
+
*/
|
|
258
|
+
|
|
259
|
+
function useActor(logic, options) {
|
|
260
|
+
const readyActor = isActor(logic);
|
|
261
|
+
if (!readyActor && !!logic && 'send' in logic && typeof logic.send === 'function') {
|
|
262
|
+
throw new Error(`useActor() expects actor logic (e.g. a machine), but received an ActorRef. Pass an Actor created with createActor()/restoreActor(), or use the useSelector(actorRef, ...) hook to read an ActorRef's snapshot.`);
|
|
231
263
|
}
|
|
232
|
-
const [actorRef, setActorRef] = useIdleActorRef(logic, options);
|
|
264
|
+
const [actorRef, setActorRef] = useIdleActorRef(logic, ...[options]);
|
|
233
265
|
const getSnapshot = React.useCallback(() => {
|
|
234
266
|
return actorRef.getSnapshot();
|
|
235
267
|
}, [actorRef]);
|
|
@@ -247,7 +279,7 @@ function useActor(logic, ...[options]) {
|
|
|
247
279
|
if (snapshotWithStatus?.status === 'error') {
|
|
248
280
|
throw snapshotWithStatus.error;
|
|
249
281
|
}
|
|
250
|
-
useActorLifecycle(actorRef, setActorRef, () => xstate.createActor(actorRef.logic, options));
|
|
282
|
+
useActorLifecycle(actorRef, setActorRef, () => xstate.createActor(actorRef.logic, readyActor ? actorRef.options : options));
|
|
251
283
|
return [actorSnapshot, actorRef.send, actorRef];
|
|
252
284
|
}
|
|
253
285
|
|
|
@@ -257,7 +289,8 @@ function useActor(logic, ...[options]) {
|
|
|
257
289
|
* @deprecated Use `useActor(machine, options)`.
|
|
258
290
|
* @public
|
|
259
291
|
*/
|
|
260
|
-
|
|
292
|
+
|
|
293
|
+
function useMachine(machine, options) {
|
|
261
294
|
return useActor(machine, options);
|
|
262
295
|
}
|
|
263
296
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import { useEffect, useState, useMemo, useRef, useCallback } from 'react';
|
|
3
3
|
import useIsomorphicLayoutEffect from 'use-isomorphic-layout-effect';
|
|
4
|
-
import { _toObserver, createActor, _hotSwapActorLogic } from 'xstate';
|
|
4
|
+
import { _toObserver, createActor, _hotSwapActorLogic, Actor } from 'xstate';
|
|
5
5
|
import { useSyncExternalStoreWithSelector } from 'use-sync-external-store/shim/with-selector';
|
|
6
6
|
import { useSyncExternalStore } from 'use-sync-external-store/shim';
|
|
7
7
|
|
|
@@ -9,15 +9,23 @@ function isStateMachine(logic) {
|
|
|
9
9
|
return typeof logic.provide === 'function' && !!logic.config && !!logic.sources;
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
+
/**
|
|
13
|
+
* An already-created actor (e.g. from `restoreActor`). Hooks use it as is
|
|
14
|
+
* instead of creating one from logic.
|
|
15
|
+
*/
|
|
16
|
+
function isActor(value) {
|
|
17
|
+
return value instanceof Actor;
|
|
18
|
+
}
|
|
19
|
+
|
|
12
20
|
// The hook updates the sources of its machine on later renders. A per-hook
|
|
13
21
|
// copy keeps those updates from reaching the caller's machine, which may be
|
|
14
22
|
// shared by other actors.
|
|
15
23
|
function ownLogic(logic) {
|
|
16
24
|
return isStateMachine(logic) ? logic.provide({}) : logic;
|
|
17
25
|
}
|
|
18
|
-
function useIdleActorRef(
|
|
26
|
+
function useIdleActorRef(logicOrActor, ...[options]) {
|
|
19
27
|
const [actorRef, setActorRef] = useState(() => {
|
|
20
|
-
return createActor(ownLogic(
|
|
28
|
+
return isActor(logicOrActor) ? logicOrActor : createActor(ownLogic(logicOrActor), options);
|
|
21
29
|
});
|
|
22
30
|
// An object whose identity changes only when React Fast Refresh re-renders
|
|
23
31
|
// this component. Fast Refresh ignores dependency lists while it applies an
|
|
@@ -31,6 +39,11 @@ function useIdleActorRef(logic, ...[options]) {
|
|
|
31
39
|
// `machine.provide()` for the same machine config.
|
|
32
40
|
// TODO: consider using `useAsapEffect` that would do this in `useInsertionEffect` is that's available
|
|
33
41
|
useIsomorphicLayoutEffect(() => {
|
|
42
|
+
// A ready actor is owned as is; its logic is not updated by later renders.
|
|
43
|
+
if (isActor(logicOrActor)) {
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
const logic = logicOrActor;
|
|
34
47
|
const currentLogic = actorRef.logic;
|
|
35
48
|
const refreshed = refreshSignalRef.current !== refreshSignal;
|
|
36
49
|
refreshSignalRef.current = refreshSignal;
|
|
@@ -88,9 +101,19 @@ function useActorLifecycle(actorRef, setActorRef, createReplacement) {
|
|
|
88
101
|
}, [actorRef]);
|
|
89
102
|
}
|
|
90
103
|
|
|
91
|
-
/**
|
|
92
|
-
|
|
93
|
-
|
|
104
|
+
/**
|
|
105
|
+
* Creates an actor from `logic` (or uses an already-created `actor`, e.g. from
|
|
106
|
+
* `restoreActor`), starts it on mount and stops it on unmount. Create a passed
|
|
107
|
+
* actor once, e.g. `useState(() => restoreActor(machine, persisted))[0]`.
|
|
108
|
+
*
|
|
109
|
+
* @public
|
|
110
|
+
*/
|
|
111
|
+
|
|
112
|
+
function useActorRef(logicOrActor, ...args) {
|
|
113
|
+
const readyActor = isActor(logicOrActor);
|
|
114
|
+
const options = readyActor ? undefined : args[0];
|
|
115
|
+
const observerOrListener = readyActor ? args[0] : args[1];
|
|
116
|
+
const [actorRef, setActorRef] = useIdleActorRef(logicOrActor, options);
|
|
94
117
|
useEffect(() => {
|
|
95
118
|
if (!observerOrListener) {
|
|
96
119
|
return;
|
|
@@ -140,9 +163,10 @@ function createActorContext(actorLogic, actorOptions) {
|
|
|
140
163
|
function Provider({
|
|
141
164
|
children,
|
|
142
165
|
logic: providedLogic = actorLogic,
|
|
143
|
-
options: providedOptions
|
|
166
|
+
options: providedOptions,
|
|
167
|
+
actor: providedActor
|
|
144
168
|
}) {
|
|
145
|
-
const actor = useActorRef(providedLogic, {
|
|
169
|
+
const actor = useActorRef(providedActor ?? providedLogic, providedActor ? undefined : {
|
|
146
170
|
...actorOptions,
|
|
147
171
|
...providedOptions
|
|
148
172
|
});
|
|
@@ -198,12 +222,20 @@ function shallowEqual(objA, objB) {
|
|
|
198
222
|
return true;
|
|
199
223
|
}
|
|
200
224
|
|
|
201
|
-
/**
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
225
|
+
/**
|
|
226
|
+
* Creates an actor from `logic` (or uses an already-created `actor`, e.g. from
|
|
227
|
+
* `restoreActor`) and subscribes to its snapshot. Create a passed actor once,
|
|
228
|
+
* e.g. `useState(() => restoreActor(machine, persisted))[0]`.
|
|
229
|
+
*
|
|
230
|
+
* @public
|
|
231
|
+
*/
|
|
232
|
+
|
|
233
|
+
function useActor(logic, options) {
|
|
234
|
+
const readyActor = isActor(logic);
|
|
235
|
+
if (!readyActor && !!logic && 'send' in logic && typeof logic.send === 'function') {
|
|
236
|
+
throw new Error(`useActor() expects actor logic (e.g. a machine), but received an ActorRef. Pass an Actor created with createActor()/restoreActor(), or use the useSelector(actorRef, ...) hook to read an ActorRef's snapshot.`);
|
|
205
237
|
}
|
|
206
|
-
const [actorRef, setActorRef] = useIdleActorRef(logic, options);
|
|
238
|
+
const [actorRef, setActorRef] = useIdleActorRef(logic, ...[options]);
|
|
207
239
|
const getSnapshot = useCallback(() => {
|
|
208
240
|
return actorRef.getSnapshot();
|
|
209
241
|
}, [actorRef]);
|
|
@@ -221,7 +253,7 @@ function useActor(logic, ...[options]) {
|
|
|
221
253
|
if (snapshotWithStatus?.status === 'error') {
|
|
222
254
|
throw snapshotWithStatus.error;
|
|
223
255
|
}
|
|
224
|
-
useActorLifecycle(actorRef, setActorRef, () => createActor(actorRef.logic, options));
|
|
256
|
+
useActorLifecycle(actorRef, setActorRef, () => createActor(actorRef.logic, readyActor ? actorRef.options : options));
|
|
225
257
|
return [actorSnapshot, actorRef.send, actorRef];
|
|
226
258
|
}
|
|
227
259
|
|
|
@@ -231,7 +263,8 @@ function useActor(logic, ...[options]) {
|
|
|
231
263
|
* @deprecated Use `useActor(machine, options)`.
|
|
232
264
|
* @public
|
|
233
265
|
*/
|
|
234
|
-
|
|
266
|
+
|
|
267
|
+
function useMachine(machine, options) {
|
|
235
268
|
return useActor(machine, options);
|
|
236
269
|
}
|
|
237
270
|
|
package/dist/xstate-react.esm.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import { useEffect, useState, useMemo, useRef, useCallback } from 'react';
|
|
3
3
|
import useIsomorphicLayoutEffect from 'use-isomorphic-layout-effect';
|
|
4
|
-
import { _toObserver, createActor } from 'xstate';
|
|
4
|
+
import { _toObserver, createActor, Actor } from 'xstate';
|
|
5
5
|
import { useSyncExternalStoreWithSelector } from 'use-sync-external-store/shim/with-selector';
|
|
6
6
|
import { useSyncExternalStore } from 'use-sync-external-store/shim';
|
|
7
7
|
|
|
@@ -9,15 +9,23 @@ function isStateMachine(logic) {
|
|
|
9
9
|
return typeof logic.provide === 'function' && !!logic.config && !!logic.sources;
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
+
/**
|
|
13
|
+
* An already-created actor (e.g. from `restoreActor`). Hooks use it as is
|
|
14
|
+
* instead of creating one from logic.
|
|
15
|
+
*/
|
|
16
|
+
function isActor(value) {
|
|
17
|
+
return value instanceof Actor;
|
|
18
|
+
}
|
|
19
|
+
|
|
12
20
|
// The hook updates the sources of its machine on later renders. A per-hook
|
|
13
21
|
// copy keeps those updates from reaching the caller's machine, which may be
|
|
14
22
|
// shared by other actors.
|
|
15
23
|
function ownLogic(logic) {
|
|
16
24
|
return isStateMachine(logic) ? logic.provide({}) : logic;
|
|
17
25
|
}
|
|
18
|
-
function useIdleActorRef(
|
|
26
|
+
function useIdleActorRef(logicOrActor, ...[options]) {
|
|
19
27
|
const [actorRef, setActorRef] = useState(() => {
|
|
20
|
-
return createActor(ownLogic(
|
|
28
|
+
return isActor(logicOrActor) ? logicOrActor : createActor(ownLogic(logicOrActor), options);
|
|
21
29
|
});
|
|
22
30
|
// An object whose identity changes only when React Fast Refresh re-renders
|
|
23
31
|
// this component. Fast Refresh ignores dependency lists while it applies an
|
|
@@ -31,6 +39,11 @@ function useIdleActorRef(logic, ...[options]) {
|
|
|
31
39
|
// `machine.provide()` for the same machine config.
|
|
32
40
|
// TODO: consider using `useAsapEffect` that would do this in `useInsertionEffect` is that's available
|
|
33
41
|
useIsomorphicLayoutEffect(() => {
|
|
42
|
+
// A ready actor is owned as is; its logic is not updated by later renders.
|
|
43
|
+
if (isActor(logicOrActor)) {
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
const logic = logicOrActor;
|
|
34
47
|
const currentLogic = actorRef.logic;
|
|
35
48
|
refreshSignalRef.current !== refreshSignal;
|
|
36
49
|
refreshSignalRef.current = refreshSignal;
|
|
@@ -78,9 +91,19 @@ function useActorLifecycle(actorRef, setActorRef, createReplacement) {
|
|
|
78
91
|
}, [actorRef]);
|
|
79
92
|
}
|
|
80
93
|
|
|
81
|
-
/**
|
|
82
|
-
|
|
83
|
-
|
|
94
|
+
/**
|
|
95
|
+
* Creates an actor from `logic` (or uses an already-created `actor`, e.g. from
|
|
96
|
+
* `restoreActor`), starts it on mount and stops it on unmount. Create a passed
|
|
97
|
+
* actor once, e.g. `useState(() => restoreActor(machine, persisted))[0]`.
|
|
98
|
+
*
|
|
99
|
+
* @public
|
|
100
|
+
*/
|
|
101
|
+
|
|
102
|
+
function useActorRef(logicOrActor, ...args) {
|
|
103
|
+
const readyActor = isActor(logicOrActor);
|
|
104
|
+
const options = readyActor ? undefined : args[0];
|
|
105
|
+
const observerOrListener = readyActor ? args[0] : args[1];
|
|
106
|
+
const [actorRef, setActorRef] = useIdleActorRef(logicOrActor, options);
|
|
84
107
|
useEffect(() => {
|
|
85
108
|
if (!observerOrListener) {
|
|
86
109
|
return;
|
|
@@ -130,9 +153,10 @@ function createActorContext(actorLogic, actorOptions) {
|
|
|
130
153
|
function Provider({
|
|
131
154
|
children,
|
|
132
155
|
logic: providedLogic = actorLogic,
|
|
133
|
-
options: providedOptions
|
|
156
|
+
options: providedOptions,
|
|
157
|
+
actor: providedActor
|
|
134
158
|
}) {
|
|
135
|
-
const actor = useActorRef(providedLogic, {
|
|
159
|
+
const actor = useActorRef(providedActor ?? providedLogic, providedActor ? undefined : {
|
|
136
160
|
...actorOptions,
|
|
137
161
|
...providedOptions
|
|
138
162
|
});
|
|
@@ -188,9 +212,17 @@ function shallowEqual(objA, objB) {
|
|
|
188
212
|
return true;
|
|
189
213
|
}
|
|
190
214
|
|
|
191
|
-
/**
|
|
192
|
-
|
|
193
|
-
|
|
215
|
+
/**
|
|
216
|
+
* Creates an actor from `logic` (or uses an already-created `actor`, e.g. from
|
|
217
|
+
* `restoreActor`) and subscribes to its snapshot. Create a passed actor once,
|
|
218
|
+
* e.g. `useState(() => restoreActor(machine, persisted))[0]`.
|
|
219
|
+
*
|
|
220
|
+
* @public
|
|
221
|
+
*/
|
|
222
|
+
|
|
223
|
+
function useActor(logic, options) {
|
|
224
|
+
const readyActor = isActor(logic);
|
|
225
|
+
const [actorRef, setActorRef] = useIdleActorRef(logic, ...[options]);
|
|
194
226
|
const getSnapshot = useCallback(() => {
|
|
195
227
|
return actorRef.getSnapshot();
|
|
196
228
|
}, [actorRef]);
|
|
@@ -208,7 +240,7 @@ function useActor(logic, ...[options]) {
|
|
|
208
240
|
if (snapshotWithStatus?.status === 'error') {
|
|
209
241
|
throw snapshotWithStatus.error;
|
|
210
242
|
}
|
|
211
|
-
useActorLifecycle(actorRef, setActorRef, () => createActor(actorRef.logic, options));
|
|
243
|
+
useActorLifecycle(actorRef, setActorRef, () => createActor(actorRef.logic, readyActor ? actorRef.options : options));
|
|
212
244
|
return [actorSnapshot, actorRef.send, actorRef];
|
|
213
245
|
}
|
|
214
246
|
|
|
@@ -218,7 +250,8 @@ function useActor(logic, ...[options]) {
|
|
|
218
250
|
* @deprecated Use `useActor(machine, options)`.
|
|
219
251
|
* @public
|
|
220
252
|
*/
|
|
221
|
-
|
|
253
|
+
|
|
254
|
+
function useMachine(machine, options) {
|
|
222
255
|
return useActor(machine, options);
|
|
223
256
|
}
|
|
224
257
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xstate/react",
|
|
3
|
-
"version": "7.0.0-
|
|
3
|
+
"version": "7.0.0-beta.0",
|
|
4
4
|
"description": "XState tools for React",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"state",
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
},
|
|
55
55
|
"peerDependencies": {
|
|
56
56
|
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
|
|
57
|
-
"xstate": "^6.0.0-
|
|
57
|
+
"xstate": "^6.0.0-beta.0"
|
|
58
58
|
},
|
|
59
59
|
"dependencies": {
|
|
60
60
|
"use-isomorphic-layout-effect": "^1.1.2",
|
|
@@ -69,7 +69,7 @@
|
|
|
69
69
|
"react-dom": "^19.0.0",
|
|
70
70
|
"rxjs": "^7.8.1",
|
|
71
71
|
"zod": "^3.25.51",
|
|
72
|
-
"xstate": "^6.0.0-
|
|
72
|
+
"xstate": "^6.0.0-beta.0"
|
|
73
73
|
},
|
|
74
74
|
"scripts": {}
|
|
75
75
|
}
|