@xstate/react 6.1.0 → 7.0.0-alpha.1
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.
|
@@ -3,7 +3,7 @@ export declare function useIdleActorRef<TLogic extends AnyActorLogic>(logic: TLo
|
|
|
3
3
|
options?: ActorOptions<TLogic> & {
|
|
4
4
|
[K in RequiredActorOptionsKeys<TLogic>]: unknown;
|
|
5
5
|
}
|
|
6
|
-
], IsNotNever<RequiredActorOptionsKeys<TLogic>>>): Actor<TLogic
|
|
6
|
+
], IsNotNever<RequiredActorOptionsKeys<TLogic>>>): [Actor<TLogic>, (actorRef: Actor<TLogic>) => void];
|
|
7
7
|
export declare function useActorRef<TLogic extends AnyActorLogic>(machine: TLogic, ...[options, observerOrListener]: IsNotNever<RequiredActorOptionsKeys<TLogic>> extends true ? [
|
|
8
8
|
options: ActorOptions<TLogic> & {
|
|
9
9
|
[K in RequiredActorOptionsKeys<TLogic>]: unknown;
|
package/dist/xstate-react.cjs.js
CHANGED
|
@@ -31,51 +31,18 @@ function _interopNamespace(e) {
|
|
|
31
31
|
var React__namespace = /*#__PURE__*/_interopNamespace(React);
|
|
32
32
|
var useIsomorphicLayoutEffect__default = /*#__PURE__*/_interopDefault(useIsomorphicLayoutEffect);
|
|
33
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 snapshot 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 snapshot
|
|
52
|
-
// React reconnects its subscribers (from the useSyncExternalStore) on its own
|
|
53
|
-
// and userland subscribers 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
|
-
const systemSnapshot = actorRef.system.getSnapshot?.();
|
|
58
|
-
actorRef.stop();
|
|
59
|
-
actorRef.system._snapshot = systemSnapshot;
|
|
60
|
-
persistedSnapshots.forEach(([ref, snapshot]) => {
|
|
61
|
-
ref._processingStatus = 0;
|
|
62
|
-
ref._snapshot = snapshot;
|
|
63
|
-
});
|
|
64
|
-
}
|
|
65
|
-
|
|
66
34
|
function useIdleActorRef(logic, ...[options]) {
|
|
67
|
-
let [
|
|
68
|
-
|
|
69
|
-
return [logic.config, actorRef];
|
|
35
|
+
let [actorRef, setActorRef] = React.useState(() => {
|
|
36
|
+
return xstate.createActor(logic, options);
|
|
70
37
|
});
|
|
71
|
-
if (logic.config !==
|
|
38
|
+
if (logic.config !== actorRef.logic.config) {
|
|
72
39
|
const newActorRef = xstate.createActor(logic, {
|
|
73
40
|
...options,
|
|
74
41
|
snapshot: actorRef.getPersistedSnapshot({
|
|
75
42
|
__unsafeAllowInlineActors: true
|
|
76
43
|
})
|
|
77
44
|
});
|
|
78
|
-
|
|
45
|
+
setActorRef(newActorRef);
|
|
79
46
|
actorRef = newActorRef;
|
|
80
47
|
}
|
|
81
48
|
|
|
@@ -83,10 +50,10 @@ function useIdleActorRef(logic, ...[options]) {
|
|
|
83
50
|
useIsomorphicLayoutEffect__default["default"](() => {
|
|
84
51
|
actorRef.logic.implementations = logic.implementations;
|
|
85
52
|
});
|
|
86
|
-
return actorRef;
|
|
53
|
+
return [actorRef, setActorRef];
|
|
87
54
|
}
|
|
88
55
|
function useActorRef(machine, ...[options, observerOrListener]) {
|
|
89
|
-
const actorRef = useIdleActorRef(machine, options);
|
|
56
|
+
const [actorRef, setActorRef] = useIdleActorRef(machine, options);
|
|
90
57
|
React.useEffect(() => {
|
|
91
58
|
if (!observerOrListener) {
|
|
92
59
|
return;
|
|
@@ -97,9 +64,22 @@ function useActorRef(machine, ...[options, observerOrListener]) {
|
|
|
97
64
|
};
|
|
98
65
|
}, [observerOrListener]);
|
|
99
66
|
React.useEffect(() => {
|
|
67
|
+
// If the actor was stopped by a previous cleanup (e.g. strict mode),
|
|
68
|
+
// create a fresh actor. The setActorRef triggers a re-render so
|
|
69
|
+
// useSyncExternalStore re-subscribes to the new actor.
|
|
70
|
+
// Only recreate if externally stopped ('stopped' status from XSTATE_STOP),
|
|
71
|
+
// not if it completed naturally ('done'/'error' status).
|
|
72
|
+
if (actorRef._processingStatus === 2 /* ProcessingStatus.Stopped */ && actorRef.getSnapshot()?.status === 'stopped') {
|
|
73
|
+
const newActor = xstate.createActor(machine, actorRef.options);
|
|
74
|
+
newActor.start();
|
|
75
|
+
setActorRef(newActor);
|
|
76
|
+
// No cleanup — the re-render will run this effect again with the
|
|
77
|
+
// new actorRef (which is Running), registering the stop cleanup then.
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
100
80
|
actorRef.start();
|
|
101
81
|
return () => {
|
|
102
|
-
|
|
82
|
+
actorRef.stop();
|
|
103
83
|
};
|
|
104
84
|
}, [actorRef]);
|
|
105
85
|
return actorRef;
|
|
@@ -199,7 +179,7 @@ function shallowEqual(objA, objB) {
|
|
|
199
179
|
}
|
|
200
180
|
|
|
201
181
|
function useActor(logic, ...[options]) {
|
|
202
|
-
const actorRef = useIdleActorRef(logic, options);
|
|
182
|
+
const [actorRef, setActorRef] = useIdleActorRef(logic, options);
|
|
203
183
|
const getSnapshot = React.useCallback(() => {
|
|
204
184
|
return actorRef.getSnapshot();
|
|
205
185
|
}, [actorRef]);
|
|
@@ -218,9 +198,15 @@ function useActor(logic, ...[options]) {
|
|
|
218
198
|
throw snapshotWithStatus.error;
|
|
219
199
|
}
|
|
220
200
|
React.useEffect(() => {
|
|
201
|
+
if (actorRef._processingStatus === 2 /* ProcessingStatus.Stopped */ && actorRef.getSnapshot()?.status === 'stopped') {
|
|
202
|
+
const newActor = xstate.createActor(logic, options);
|
|
203
|
+
newActor.start();
|
|
204
|
+
setActorRef(newActor);
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
221
207
|
actorRef.start();
|
|
222
208
|
return () => {
|
|
223
|
-
|
|
209
|
+
actorRef.stop();
|
|
224
210
|
};
|
|
225
211
|
}, [actorRef]);
|
|
226
212
|
return [actorSnapshot, actorRef.send, actorRef];
|
|
@@ -31,51 +31,18 @@ function _interopNamespace(e) {
|
|
|
31
31
|
var React__namespace = /*#__PURE__*/_interopNamespace(React);
|
|
32
32
|
var useIsomorphicLayoutEffect__default = /*#__PURE__*/_interopDefault(useIsomorphicLayoutEffect);
|
|
33
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 snapshot 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 snapshot
|
|
52
|
-
// React reconnects its subscribers (from the useSyncExternalStore) on its own
|
|
53
|
-
// and userland subscribers 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
|
-
const systemSnapshot = actorRef.system.getSnapshot?.();
|
|
58
|
-
actorRef.stop();
|
|
59
|
-
actorRef.system._snapshot = systemSnapshot;
|
|
60
|
-
persistedSnapshots.forEach(([ref, snapshot]) => {
|
|
61
|
-
ref._processingStatus = 0;
|
|
62
|
-
ref._snapshot = snapshot;
|
|
63
|
-
});
|
|
64
|
-
}
|
|
65
|
-
|
|
66
34
|
function useIdleActorRef(logic, ...[options]) {
|
|
67
|
-
let [
|
|
68
|
-
|
|
69
|
-
return [logic.config, actorRef];
|
|
35
|
+
let [actorRef, setActorRef] = React.useState(() => {
|
|
36
|
+
return xstate.createActor(logic, options);
|
|
70
37
|
});
|
|
71
|
-
if (logic.config !==
|
|
38
|
+
if (logic.config !== actorRef.logic.config) {
|
|
72
39
|
const newActorRef = xstate.createActor(logic, {
|
|
73
40
|
...options,
|
|
74
41
|
snapshot: actorRef.getPersistedSnapshot({
|
|
75
42
|
__unsafeAllowInlineActors: true
|
|
76
43
|
})
|
|
77
44
|
});
|
|
78
|
-
|
|
45
|
+
setActorRef(newActorRef);
|
|
79
46
|
actorRef = newActorRef;
|
|
80
47
|
}
|
|
81
48
|
|
|
@@ -83,10 +50,10 @@ function useIdleActorRef(logic, ...[options]) {
|
|
|
83
50
|
useIsomorphicLayoutEffect__default["default"](() => {
|
|
84
51
|
actorRef.logic.implementations = logic.implementations;
|
|
85
52
|
});
|
|
86
|
-
return actorRef;
|
|
53
|
+
return [actorRef, setActorRef];
|
|
87
54
|
}
|
|
88
55
|
function useActorRef(machine, ...[options, observerOrListener]) {
|
|
89
|
-
const actorRef = useIdleActorRef(machine, options);
|
|
56
|
+
const [actorRef, setActorRef] = useIdleActorRef(machine, options);
|
|
90
57
|
React.useEffect(() => {
|
|
91
58
|
if (!observerOrListener) {
|
|
92
59
|
return;
|
|
@@ -97,9 +64,22 @@ function useActorRef(machine, ...[options, observerOrListener]) {
|
|
|
97
64
|
};
|
|
98
65
|
}, [observerOrListener]);
|
|
99
66
|
React.useEffect(() => {
|
|
67
|
+
// If the actor was stopped by a previous cleanup (e.g. strict mode),
|
|
68
|
+
// create a fresh actor. The setActorRef triggers a re-render so
|
|
69
|
+
// useSyncExternalStore re-subscribes to the new actor.
|
|
70
|
+
// Only recreate if externally stopped ('stopped' status from XSTATE_STOP),
|
|
71
|
+
// not if it completed naturally ('done'/'error' status).
|
|
72
|
+
if (actorRef._processingStatus === 2 /* ProcessingStatus.Stopped */ && actorRef.getSnapshot()?.status === 'stopped') {
|
|
73
|
+
const newActor = xstate.createActor(machine, actorRef.options);
|
|
74
|
+
newActor.start();
|
|
75
|
+
setActorRef(newActor);
|
|
76
|
+
// No cleanup — the re-render will run this effect again with the
|
|
77
|
+
// new actorRef (which is Running), registering the stop cleanup then.
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
100
80
|
actorRef.start();
|
|
101
81
|
return () => {
|
|
102
|
-
|
|
82
|
+
actorRef.stop();
|
|
103
83
|
};
|
|
104
84
|
}, [actorRef]);
|
|
105
85
|
return actorRef;
|
|
@@ -202,7 +182,7 @@ function useActor(logic, ...[options]) {
|
|
|
202
182
|
if (!!logic && 'send' in logic && typeof logic.send === 'function') {
|
|
203
183
|
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.`);
|
|
204
184
|
}
|
|
205
|
-
const actorRef = useIdleActorRef(logic, options);
|
|
185
|
+
const [actorRef, setActorRef] = useIdleActorRef(logic, options);
|
|
206
186
|
const getSnapshot = React.useCallback(() => {
|
|
207
187
|
return actorRef.getSnapshot();
|
|
208
188
|
}, [actorRef]);
|
|
@@ -221,9 +201,15 @@ function useActor(logic, ...[options]) {
|
|
|
221
201
|
throw snapshotWithStatus.error;
|
|
222
202
|
}
|
|
223
203
|
React.useEffect(() => {
|
|
204
|
+
if (actorRef._processingStatus === 2 /* ProcessingStatus.Stopped */ && actorRef.getSnapshot()?.status === 'stopped') {
|
|
205
|
+
const newActor = xstate.createActor(logic, options);
|
|
206
|
+
newActor.start();
|
|
207
|
+
setActorRef(newActor);
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
224
210
|
actorRef.start();
|
|
225
211
|
return () => {
|
|
226
|
-
|
|
212
|
+
actorRef.stop();
|
|
227
213
|
};
|
|
228
214
|
}, [actorRef]);
|
|
229
215
|
return [actorSnapshot, actorRef.send, actorRef];
|
|
@@ -5,51 +5,18 @@ import { toObserver, createActor } 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
|
|
|
8
|
-
const forEachActor = (actorRef, callback) => {
|
|
9
|
-
callback(actorRef);
|
|
10
|
-
const children = actorRef.getSnapshot().children;
|
|
11
|
-
if (children) {
|
|
12
|
-
Object.values(children).forEach(child => {
|
|
13
|
-
forEachActor(child, callback);
|
|
14
|
-
});
|
|
15
|
-
}
|
|
16
|
-
};
|
|
17
|
-
function stopRootWithRehydration(actorRef) {
|
|
18
|
-
// persist snapshot here in a custom way allows us to persist inline actors and to preserve actor references
|
|
19
|
-
// we do it to avoid setState in useEffect when the effect gets "reconnected"
|
|
20
|
-
// this currently only happens in Strict Effects but it simulates the Offscreen aka Activity API
|
|
21
|
-
// it also just allows us to end up with a somewhat more predictable behavior for the users
|
|
22
|
-
const persistedSnapshots = [];
|
|
23
|
-
forEachActor(actorRef, ref => {
|
|
24
|
-
persistedSnapshots.push([ref, ref.getSnapshot()]);
|
|
25
|
-
// muting observers allow us to avoid `useSelector` from being notified about the stopped snapshot
|
|
26
|
-
// React reconnects its subscribers (from the useSyncExternalStore) on its own
|
|
27
|
-
// and userland subscribers should basically always do the same anyway
|
|
28
|
-
// as each subscription should have its own cleanup logic and that should be called each such reconnect
|
|
29
|
-
ref.observers = new Set();
|
|
30
|
-
});
|
|
31
|
-
const systemSnapshot = actorRef.system.getSnapshot?.();
|
|
32
|
-
actorRef.stop();
|
|
33
|
-
actorRef.system._snapshot = systemSnapshot;
|
|
34
|
-
persistedSnapshots.forEach(([ref, snapshot]) => {
|
|
35
|
-
ref._processingStatus = 0;
|
|
36
|
-
ref._snapshot = snapshot;
|
|
37
|
-
});
|
|
38
|
-
}
|
|
39
|
-
|
|
40
8
|
function useIdleActorRef(logic, ...[options]) {
|
|
41
|
-
let [
|
|
42
|
-
|
|
43
|
-
return [logic.config, actorRef];
|
|
9
|
+
let [actorRef, setActorRef] = useState(() => {
|
|
10
|
+
return createActor(logic, options);
|
|
44
11
|
});
|
|
45
|
-
if (logic.config !==
|
|
12
|
+
if (logic.config !== actorRef.logic.config) {
|
|
46
13
|
const newActorRef = createActor(logic, {
|
|
47
14
|
...options,
|
|
48
15
|
snapshot: actorRef.getPersistedSnapshot({
|
|
49
16
|
__unsafeAllowInlineActors: true
|
|
50
17
|
})
|
|
51
18
|
});
|
|
52
|
-
|
|
19
|
+
setActorRef(newActorRef);
|
|
53
20
|
actorRef = newActorRef;
|
|
54
21
|
}
|
|
55
22
|
|
|
@@ -57,10 +24,10 @@ function useIdleActorRef(logic, ...[options]) {
|
|
|
57
24
|
useIsomorphicLayoutEffect(() => {
|
|
58
25
|
actorRef.logic.implementations = logic.implementations;
|
|
59
26
|
});
|
|
60
|
-
return actorRef;
|
|
27
|
+
return [actorRef, setActorRef];
|
|
61
28
|
}
|
|
62
29
|
function useActorRef(machine, ...[options, observerOrListener]) {
|
|
63
|
-
const actorRef = useIdleActorRef(machine, options);
|
|
30
|
+
const [actorRef, setActorRef] = useIdleActorRef(machine, options);
|
|
64
31
|
useEffect(() => {
|
|
65
32
|
if (!observerOrListener) {
|
|
66
33
|
return;
|
|
@@ -71,9 +38,22 @@ function useActorRef(machine, ...[options, observerOrListener]) {
|
|
|
71
38
|
};
|
|
72
39
|
}, [observerOrListener]);
|
|
73
40
|
useEffect(() => {
|
|
41
|
+
// If the actor was stopped by a previous cleanup (e.g. strict mode),
|
|
42
|
+
// create a fresh actor. The setActorRef triggers a re-render so
|
|
43
|
+
// useSyncExternalStore re-subscribes to the new actor.
|
|
44
|
+
// Only recreate if externally stopped ('stopped' status from XSTATE_STOP),
|
|
45
|
+
// not if it completed naturally ('done'/'error' status).
|
|
46
|
+
if (actorRef._processingStatus === 2 /* ProcessingStatus.Stopped */ && actorRef.getSnapshot()?.status === 'stopped') {
|
|
47
|
+
const newActor = createActor(machine, actorRef.options);
|
|
48
|
+
newActor.start();
|
|
49
|
+
setActorRef(newActor);
|
|
50
|
+
// No cleanup — the re-render will run this effect again with the
|
|
51
|
+
// new actorRef (which is Running), registering the stop cleanup then.
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
74
54
|
actorRef.start();
|
|
75
55
|
return () => {
|
|
76
|
-
|
|
56
|
+
actorRef.stop();
|
|
77
57
|
};
|
|
78
58
|
}, [actorRef]);
|
|
79
59
|
return actorRef;
|
|
@@ -176,7 +156,7 @@ function useActor(logic, ...[options]) {
|
|
|
176
156
|
if (!!logic && 'send' in logic && typeof logic.send === 'function') {
|
|
177
157
|
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.`);
|
|
178
158
|
}
|
|
179
|
-
const actorRef = useIdleActorRef(logic, options);
|
|
159
|
+
const [actorRef, setActorRef] = useIdleActorRef(logic, options);
|
|
180
160
|
const getSnapshot = useCallback(() => {
|
|
181
161
|
return actorRef.getSnapshot();
|
|
182
162
|
}, [actorRef]);
|
|
@@ -195,9 +175,15 @@ function useActor(logic, ...[options]) {
|
|
|
195
175
|
throw snapshotWithStatus.error;
|
|
196
176
|
}
|
|
197
177
|
useEffect(() => {
|
|
178
|
+
if (actorRef._processingStatus === 2 /* ProcessingStatus.Stopped */ && actorRef.getSnapshot()?.status === 'stopped') {
|
|
179
|
+
const newActor = createActor(logic, options);
|
|
180
|
+
newActor.start();
|
|
181
|
+
setActorRef(newActor);
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
198
184
|
actorRef.start();
|
|
199
185
|
return () => {
|
|
200
|
-
|
|
186
|
+
actorRef.stop();
|
|
201
187
|
};
|
|
202
188
|
}, [actorRef]);
|
|
203
189
|
return [actorSnapshot, actorRef.send, actorRef];
|
package/dist/xstate-react.esm.js
CHANGED
|
@@ -5,51 +5,18 @@ import { toObserver, createActor } 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
|
|
|
8
|
-
const forEachActor = (actorRef, callback) => {
|
|
9
|
-
callback(actorRef);
|
|
10
|
-
const children = actorRef.getSnapshot().children;
|
|
11
|
-
if (children) {
|
|
12
|
-
Object.values(children).forEach(child => {
|
|
13
|
-
forEachActor(child, callback);
|
|
14
|
-
});
|
|
15
|
-
}
|
|
16
|
-
};
|
|
17
|
-
function stopRootWithRehydration(actorRef) {
|
|
18
|
-
// persist snapshot here in a custom way allows us to persist inline actors and to preserve actor references
|
|
19
|
-
// we do it to avoid setState in useEffect when the effect gets "reconnected"
|
|
20
|
-
// this currently only happens in Strict Effects but it simulates the Offscreen aka Activity API
|
|
21
|
-
// it also just allows us to end up with a somewhat more predictable behavior for the users
|
|
22
|
-
const persistedSnapshots = [];
|
|
23
|
-
forEachActor(actorRef, ref => {
|
|
24
|
-
persistedSnapshots.push([ref, ref.getSnapshot()]);
|
|
25
|
-
// muting observers allow us to avoid `useSelector` from being notified about the stopped snapshot
|
|
26
|
-
// React reconnects its subscribers (from the useSyncExternalStore) on its own
|
|
27
|
-
// and userland subscribers should basically always do the same anyway
|
|
28
|
-
// as each subscription should have its own cleanup logic and that should be called each such reconnect
|
|
29
|
-
ref.observers = new Set();
|
|
30
|
-
});
|
|
31
|
-
const systemSnapshot = actorRef.system.getSnapshot?.();
|
|
32
|
-
actorRef.stop();
|
|
33
|
-
actorRef.system._snapshot = systemSnapshot;
|
|
34
|
-
persistedSnapshots.forEach(([ref, snapshot]) => {
|
|
35
|
-
ref._processingStatus = 0;
|
|
36
|
-
ref._snapshot = snapshot;
|
|
37
|
-
});
|
|
38
|
-
}
|
|
39
|
-
|
|
40
8
|
function useIdleActorRef(logic, ...[options]) {
|
|
41
|
-
let [
|
|
42
|
-
|
|
43
|
-
return [logic.config, actorRef];
|
|
9
|
+
let [actorRef, setActorRef] = useState(() => {
|
|
10
|
+
return createActor(logic, options);
|
|
44
11
|
});
|
|
45
|
-
if (logic.config !==
|
|
12
|
+
if (logic.config !== actorRef.logic.config) {
|
|
46
13
|
const newActorRef = createActor(logic, {
|
|
47
14
|
...options,
|
|
48
15
|
snapshot: actorRef.getPersistedSnapshot({
|
|
49
16
|
__unsafeAllowInlineActors: true
|
|
50
17
|
})
|
|
51
18
|
});
|
|
52
|
-
|
|
19
|
+
setActorRef(newActorRef);
|
|
53
20
|
actorRef = newActorRef;
|
|
54
21
|
}
|
|
55
22
|
|
|
@@ -57,10 +24,10 @@ function useIdleActorRef(logic, ...[options]) {
|
|
|
57
24
|
useIsomorphicLayoutEffect(() => {
|
|
58
25
|
actorRef.logic.implementations = logic.implementations;
|
|
59
26
|
});
|
|
60
|
-
return actorRef;
|
|
27
|
+
return [actorRef, setActorRef];
|
|
61
28
|
}
|
|
62
29
|
function useActorRef(machine, ...[options, observerOrListener]) {
|
|
63
|
-
const actorRef = useIdleActorRef(machine, options);
|
|
30
|
+
const [actorRef, setActorRef] = useIdleActorRef(machine, options);
|
|
64
31
|
useEffect(() => {
|
|
65
32
|
if (!observerOrListener) {
|
|
66
33
|
return;
|
|
@@ -71,9 +38,22 @@ function useActorRef(machine, ...[options, observerOrListener]) {
|
|
|
71
38
|
};
|
|
72
39
|
}, [observerOrListener]);
|
|
73
40
|
useEffect(() => {
|
|
41
|
+
// If the actor was stopped by a previous cleanup (e.g. strict mode),
|
|
42
|
+
// create a fresh actor. The setActorRef triggers a re-render so
|
|
43
|
+
// useSyncExternalStore re-subscribes to the new actor.
|
|
44
|
+
// Only recreate if externally stopped ('stopped' status from XSTATE_STOP),
|
|
45
|
+
// not if it completed naturally ('done'/'error' status).
|
|
46
|
+
if (actorRef._processingStatus === 2 /* ProcessingStatus.Stopped */ && actorRef.getSnapshot()?.status === 'stopped') {
|
|
47
|
+
const newActor = createActor(machine, actorRef.options);
|
|
48
|
+
newActor.start();
|
|
49
|
+
setActorRef(newActor);
|
|
50
|
+
// No cleanup — the re-render will run this effect again with the
|
|
51
|
+
// new actorRef (which is Running), registering the stop cleanup then.
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
74
54
|
actorRef.start();
|
|
75
55
|
return () => {
|
|
76
|
-
|
|
56
|
+
actorRef.stop();
|
|
77
57
|
};
|
|
78
58
|
}, [actorRef]);
|
|
79
59
|
return actorRef;
|
|
@@ -173,7 +153,7 @@ function shallowEqual(objA, objB) {
|
|
|
173
153
|
}
|
|
174
154
|
|
|
175
155
|
function useActor(logic, ...[options]) {
|
|
176
|
-
const actorRef = useIdleActorRef(logic, options);
|
|
156
|
+
const [actorRef, setActorRef] = useIdleActorRef(logic, options);
|
|
177
157
|
const getSnapshot = useCallback(() => {
|
|
178
158
|
return actorRef.getSnapshot();
|
|
179
159
|
}, [actorRef]);
|
|
@@ -192,9 +172,15 @@ function useActor(logic, ...[options]) {
|
|
|
192
172
|
throw snapshotWithStatus.error;
|
|
193
173
|
}
|
|
194
174
|
useEffect(() => {
|
|
175
|
+
if (actorRef._processingStatus === 2 /* ProcessingStatus.Stopped */ && actorRef.getSnapshot()?.status === 'stopped') {
|
|
176
|
+
const newActor = createActor(logic, options);
|
|
177
|
+
newActor.start();
|
|
178
|
+
setActorRef(newActor);
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
195
181
|
actorRef.start();
|
|
196
182
|
return () => {
|
|
197
|
-
|
|
183
|
+
actorRef.stop();
|
|
198
184
|
};
|
|
199
185
|
}, [actorRef]);
|
|
200
186
|
return [actorSnapshot, actorRef.send, actorRef];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xstate/react",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "7.0.0-alpha.1",
|
|
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": "^
|
|
57
|
+
"xstate": "^6.0.0-alpha.1"
|
|
58
58
|
},
|
|
59
59
|
"peerDependenciesMeta": {
|
|
60
60
|
"xstate": {
|
|
@@ -73,7 +73,8 @@
|
|
|
73
73
|
"react": "^19.0.0",
|
|
74
74
|
"react-dom": "^19.0.0",
|
|
75
75
|
"rxjs": "^7.8.1",
|
|
76
|
-
"
|
|
76
|
+
"zod": "^3.25.51",
|
|
77
|
+
"xstate": "^6.0.0-alpha.1"
|
|
77
78
|
},
|
|
78
79
|
"scripts": {}
|
|
79
80
|
}
|