@xstate/react
Advanced tools
Comparing version 4.0.0-beta.10 to 4.0.0-beta.11
@@ -10,2 +10,6 @@ import * as React from 'react'; | ||
options?: ActorOptions<TLogic>; | ||
/** | ||
* @deprecated Use `logic` instead. | ||
*/ | ||
machine?: never; | ||
} & (TLogic extends AnyStateMachine ? AreAllImplementationsAssumedToBeProvided<TLogic['__TResolvedTypesMeta']> extends true ? { | ||
@@ -12,0 +16,0 @@ logic?: TLogic; |
@@ -1,14 +0,3 @@ | ||
import { AnyActorLogic, AnyActor, AnyStateMachine, AreAllImplementationsAssumedToBeProvided, InternalMachineImplementations, ActorRefFrom, ActorOptions, Observer, StateFrom, SnapshotFrom, TODO } from 'xstate'; | ||
export declare function useIdleInterpreter(machine: AnyActorLogic, options: Partial<ActorOptions<AnyActorLogic>>): AnyActor; | ||
type RestParams<TLogic extends AnyActorLogic> = TLogic extends AnyStateMachine ? AreAllImplementationsAssumedToBeProvided<TLogic['__TResolvedTypesMeta']> extends false ? [ | ||
options: ActorOptions<TLogic> & InternalMachineImplementations<TLogic['__TContext'], TLogic['__TEvent'], TODO, TODO, TODO, TLogic['__TResolvedTypesMeta'], true>, | ||
observerOrListener?: Observer<StateFrom<TLogic>> | ((value: StateFrom<TLogic>) => void) | ||
] : [ | ||
options?: ActorOptions<TLogic> & InternalMachineImplementations<TLogic['__TContext'], TLogic['__TEvent'], TODO, TODO, TODO, TLogic['__TResolvedTypesMeta']>, | ||
observerOrListener?: Observer<StateFrom<TLogic>> | ((value: StateFrom<TLogic>) => void) | ||
] : [ | ||
options?: ActorOptions<TLogic>, | ||
observerOrListener?: Observer<SnapshotFrom<TLogic>> | ((value: SnapshotFrom<TLogic>) => void) | ||
]; | ||
export declare function useActorRef<TLogic extends AnyActorLogic>(machine: TLogic, ...[options, observerOrListener]: RestParams<TLogic>): ActorRefFrom<TLogic>; | ||
export {}; | ||
import { AnyActorLogic, AnyActor, ActorRefFrom, ActorOptions, Observer, SnapshotFrom } from 'xstate'; | ||
export declare function useIdleActor(logic: AnyActorLogic, options: Partial<ActorOptions<AnyActorLogic>>): AnyActor; | ||
export declare function useActorRef<TLogic extends AnyActorLogic>(machine: TLogic, options?: ActorOptions<TLogic>, observerOrListener?: Observer<SnapshotFrom<TLogic>> | ((value: SnapshotFrom<TLogic>) => void)): ActorRefFrom<TLogic>; |
@@ -34,25 +34,56 @@ 'use strict'; | ||
function useConstant(fn) { | ||
const ref = React__namespace.useRef(); | ||
if (!ref.current) { | ||
ref.current = { | ||
v: fn() | ||
}; | ||
const forEachActor = (actorRef, callback) => { | ||
callback(actorRef); | ||
const children = actorRef.getSnapshot().children; | ||
if (children) { | ||
Object.values(children).forEach(child => { | ||
forEachActor(child, callback); | ||
}); | ||
} | ||
return ref.current.v; | ||
}; | ||
function stopRootWithRehydration(actorRef) { | ||
// persist state here in a custom way allows us to persist inline actors and to preserve actor references | ||
// we do it to avoid setState in useEffect when the effect gets "reconnected" | ||
// this currently only happens in Strict Effects but it simulates the Offscreen aka Activity API | ||
// it also just allows us to end up with a somewhat more predictable behavior for the users | ||
const persistedSnapshots = []; | ||
forEachActor(actorRef, ref => { | ||
persistedSnapshots.push([ref, ref.getSnapshot()]); | ||
// muting observers allow us to avoid `useSelector` from being notified about the stopped state | ||
// React reconnects its subscribers (from the useSyncExternalStore) on its own | ||
// and userland subscibers should basically always do the same anyway | ||
// as each subscription should have its own cleanup logic and that should be called each such reconnect | ||
ref.observers = new Set(); | ||
}); | ||
actorRef.stop(); | ||
persistedSnapshots.forEach(([ref, snapshot]) => { | ||
ref._processingStatus = 0; | ||
ref._state = snapshot; | ||
}); | ||
} | ||
function useIdleInterpreter(machine, options) { | ||
const actorRef = useConstant(() => { | ||
return xstate.createActor(machine, options); | ||
function useIdleActor(logic, options) { | ||
let [[currentConfig, actorRef], setCurrent] = React.useState(() => { | ||
const actorRef = xstate.createActor(logic, options); | ||
return [logic.config, actorRef]; | ||
}); | ||
if (logic.config !== currentConfig) { | ||
const newActorRef = xstate.createActor(logic, { | ||
...options, | ||
state: actorRef.getPersistedState({ | ||
__unsafeAllowInlineActors: true | ||
}) | ||
}); | ||
setCurrent([logic.config, newActorRef]); | ||
actorRef = newActorRef; | ||
} | ||
// TODO: consider using `useAsapEffect` that would do this in `useInsertionEffect` is that's available | ||
useIsomorphicLayoutEffect__default["default"](() => { | ||
actorRef.logic.implementations = machine.implementations; | ||
actorRef.logic.implementations = logic.implementations; | ||
}); | ||
return actorRef; | ||
} | ||
function useActorRef(machine, ...[options = {}, observerOrListener]) { | ||
const actorRef = useIdleInterpreter(machine, options); | ||
function useActorRef(machine, options = {}, observerOrListener) { | ||
const actorRef = useIdleActor(machine, options); | ||
React.useEffect(() => { | ||
@@ -70,7 +101,5 @@ if (!observerOrListener) { | ||
return () => { | ||
actorRef.stop(); | ||
actorRef.status = xstate.ActorStatus.NotStarted; | ||
actorRef._initState(); | ||
stopRootWithRehydration(actorRef); | ||
}; | ||
}, []); | ||
}, [actorRef]); | ||
return actorRef; | ||
@@ -80,3 +109,3 @@ } | ||
function useActor(logic, options = {}) { | ||
const actorRef = useIdleInterpreter(logic, options); | ||
const actorRef = useIdleActor(logic, options); | ||
const getSnapshot = React.useCallback(() => { | ||
@@ -95,5 +124,3 @@ return actorRef.getSnapshot(); | ||
return () => { | ||
actorRef.stop(); | ||
actorRef.status = xstate.ActorStatus.NotStarted; | ||
actorRef._initState(); | ||
stopRootWithRehydration(actorRef); | ||
}; | ||
@@ -100,0 +127,0 @@ }, [actorRef]); |
@@ -34,31 +34,56 @@ 'use strict'; | ||
function useConstant(fn) { | ||
const ref = React__namespace.useRef(); | ||
if (!ref.current) { | ||
ref.current = { | ||
v: fn() | ||
}; | ||
const forEachActor = (actorRef, callback) => { | ||
callback(actorRef); | ||
const children = actorRef.getSnapshot().children; | ||
if (children) { | ||
Object.values(children).forEach(child => { | ||
forEachActor(child, callback); | ||
}); | ||
} | ||
return ref.current.v; | ||
}; | ||
function stopRootWithRehydration(actorRef) { | ||
// persist state here in a custom way allows us to persist inline actors and to preserve actor references | ||
// we do it to avoid setState in useEffect when the effect gets "reconnected" | ||
// this currently only happens in Strict Effects but it simulates the Offscreen aka Activity API | ||
// it also just allows us to end up with a somewhat more predictable behavior for the users | ||
const persistedSnapshots = []; | ||
forEachActor(actorRef, ref => { | ||
persistedSnapshots.push([ref, ref.getSnapshot()]); | ||
// muting observers allow us to avoid `useSelector` from being notified about the stopped state | ||
// React reconnects its subscribers (from the useSyncExternalStore) on its own | ||
// and userland subscibers should basically always do the same anyway | ||
// as each subscription should have its own cleanup logic and that should be called each such reconnect | ||
ref.observers = new Set(); | ||
}); | ||
actorRef.stop(); | ||
persistedSnapshots.forEach(([ref, snapshot]) => { | ||
ref._processingStatus = 0; | ||
ref._state = snapshot; | ||
}); | ||
} | ||
function useIdleInterpreter(machine, options) { | ||
{ | ||
const [initialMachine] = React.useState(machine); | ||
if (machine.config !== initialMachine.config) { | ||
console.warn(`Actor logic has changed between renders. This is not supported and may lead to invalid snapshots.`); | ||
} | ||
function useIdleActor(logic, options) { | ||
let [[currentConfig, actorRef], setCurrent] = React.useState(() => { | ||
const actorRef = xstate.createActor(logic, options); | ||
return [logic.config, actorRef]; | ||
}); | ||
if (logic.config !== currentConfig) { | ||
const newActorRef = xstate.createActor(logic, { | ||
...options, | ||
state: actorRef.getPersistedState({ | ||
__unsafeAllowInlineActors: true | ||
}) | ||
}); | ||
setCurrent([logic.config, newActorRef]); | ||
actorRef = newActorRef; | ||
} | ||
const actorRef = useConstant(() => { | ||
return xstate.createActor(machine, options); | ||
}); | ||
// TODO: consider using `useAsapEffect` that would do this in `useInsertionEffect` is that's available | ||
useIsomorphicLayoutEffect__default["default"](() => { | ||
actorRef.logic.implementations = machine.implementations; | ||
actorRef.logic.implementations = logic.implementations; | ||
}); | ||
return actorRef; | ||
} | ||
function useActorRef(machine, ...[options = {}, observerOrListener]) { | ||
const actorRef = useIdleInterpreter(machine, options); | ||
function useActorRef(machine, options = {}, observerOrListener) { | ||
const actorRef = useIdleActor(machine, options); | ||
React.useEffect(() => { | ||
@@ -76,7 +101,5 @@ if (!observerOrListener) { | ||
return () => { | ||
actorRef.stop(); | ||
actorRef.status = xstate.ActorStatus.NotStarted; | ||
actorRef._initState(); | ||
stopRootWithRehydration(actorRef); | ||
}; | ||
}, []); | ||
}, [actorRef]); | ||
return actorRef; | ||
@@ -89,3 +112,3 @@ } | ||
} | ||
const actorRef = useIdleInterpreter(logic, options); | ||
const actorRef = useIdleActor(logic, options); | ||
const getSnapshot = React.useCallback(() => { | ||
@@ -104,5 +127,3 @@ return actorRef.getSnapshot(); | ||
return () => { | ||
actorRef.stop(); | ||
actorRef.status = xstate.ActorStatus.NotStarted; | ||
actorRef._initState(); | ||
stopRootWithRehydration(actorRef); | ||
}; | ||
@@ -109,0 +130,0 @@ }, [actorRef]); |
import * as React from 'react'; | ||
import { useEffect, useState, useCallback } from 'react'; | ||
import { useSyncExternalStore } from 'use-sync-external-store/shim'; | ||
import { toObserver, ActorStatus, createActor } from 'xstate'; | ||
import { toObserver, createActor } from 'xstate'; | ||
import useIsomorphicLayoutEffect from 'use-isomorphic-layout-effect'; | ||
import { useSyncExternalStoreWithSelector } from 'use-sync-external-store/shim/with-selector'; | ||
function useConstant(fn) { | ||
const ref = React.useRef(); | ||
if (!ref.current) { | ||
ref.current = { | ||
v: fn() | ||
}; | ||
const forEachActor = (actorRef, callback) => { | ||
callback(actorRef); | ||
const children = actorRef.getSnapshot().children; | ||
if (children) { | ||
Object.values(children).forEach(child => { | ||
forEachActor(child, callback); | ||
}); | ||
} | ||
return ref.current.v; | ||
}; | ||
function stopRootWithRehydration(actorRef) { | ||
// persist state here in a custom way allows us to persist inline actors and to preserve actor references | ||
// we do it to avoid setState in useEffect when the effect gets "reconnected" | ||
// this currently only happens in Strict Effects but it simulates the Offscreen aka Activity API | ||
// it also just allows us to end up with a somewhat more predictable behavior for the users | ||
const persistedSnapshots = []; | ||
forEachActor(actorRef, ref => { | ||
persistedSnapshots.push([ref, ref.getSnapshot()]); | ||
// muting observers allow us to avoid `useSelector` from being notified about the stopped state | ||
// React reconnects its subscribers (from the useSyncExternalStore) on its own | ||
// and userland subscibers should basically always do the same anyway | ||
// as each subscription should have its own cleanup logic and that should be called each such reconnect | ||
ref.observers = new Set(); | ||
}); | ||
actorRef.stop(); | ||
persistedSnapshots.forEach(([ref, snapshot]) => { | ||
ref._processingStatus = 0; | ||
ref._state = snapshot; | ||
}); | ||
} | ||
function useIdleInterpreter(machine, options) { | ||
{ | ||
const [initialMachine] = useState(machine); | ||
if (machine.config !== initialMachine.config) { | ||
console.warn(`Actor logic has changed between renders. This is not supported and may lead to invalid snapshots.`); | ||
} | ||
function useIdleActor(logic, options) { | ||
let [[currentConfig, actorRef], setCurrent] = useState(() => { | ||
const actorRef = createActor(logic, options); | ||
return [logic.config, actorRef]; | ||
}); | ||
if (logic.config !== currentConfig) { | ||
const newActorRef = createActor(logic, { | ||
...options, | ||
state: actorRef.getPersistedState({ | ||
__unsafeAllowInlineActors: true | ||
}) | ||
}); | ||
setCurrent([logic.config, newActorRef]); | ||
actorRef = newActorRef; | ||
} | ||
const actorRef = useConstant(() => { | ||
return createActor(machine, options); | ||
}); | ||
// TODO: consider using `useAsapEffect` that would do this in `useInsertionEffect` is that's available | ||
useIsomorphicLayoutEffect(() => { | ||
actorRef.logic.implementations = machine.implementations; | ||
actorRef.logic.implementations = logic.implementations; | ||
}); | ||
return actorRef; | ||
} | ||
function useActorRef(machine, ...[options = {}, observerOrListener]) { | ||
const actorRef = useIdleInterpreter(machine, options); | ||
function useActorRef(machine, options = {}, observerOrListener) { | ||
const actorRef = useIdleActor(machine, options); | ||
useEffect(() => { | ||
@@ -49,7 +74,5 @@ if (!observerOrListener) { | ||
return () => { | ||
actorRef.stop(); | ||
actorRef.status = ActorStatus.NotStarted; | ||
actorRef._initState(); | ||
stopRootWithRehydration(actorRef); | ||
}; | ||
}, []); | ||
}, [actorRef]); | ||
return actorRef; | ||
@@ -62,3 +85,3 @@ } | ||
} | ||
const actorRef = useIdleInterpreter(logic, options); | ||
const actorRef = useIdleActor(logic, options); | ||
const getSnapshot = useCallback(() => { | ||
@@ -77,5 +100,3 @@ return actorRef.getSnapshot(); | ||
return () => { | ||
actorRef.stop(); | ||
actorRef.status = ActorStatus.NotStarted; | ||
actorRef._initState(); | ||
stopRootWithRehydration(actorRef); | ||
}; | ||
@@ -82,0 +103,0 @@ }, [actorRef]); |
import * as React from 'react'; | ||
import { useEffect, useCallback } from 'react'; | ||
import { useEffect, useState, useCallback } from 'react'; | ||
import { useSyncExternalStore } from 'use-sync-external-store/shim'; | ||
import { toObserver, ActorStatus, createActor } from 'xstate'; | ||
import { toObserver, createActor } from 'xstate'; | ||
import useIsomorphicLayoutEffect from 'use-isomorphic-layout-effect'; | ||
import { useSyncExternalStoreWithSelector } from 'use-sync-external-store/shim/with-selector'; | ||
function useConstant(fn) { | ||
const ref = React.useRef(); | ||
if (!ref.current) { | ||
ref.current = { | ||
v: fn() | ||
}; | ||
const forEachActor = (actorRef, callback) => { | ||
callback(actorRef); | ||
const children = actorRef.getSnapshot().children; | ||
if (children) { | ||
Object.values(children).forEach(child => { | ||
forEachActor(child, callback); | ||
}); | ||
} | ||
return ref.current.v; | ||
}; | ||
function stopRootWithRehydration(actorRef) { | ||
// persist state here in a custom way allows us to persist inline actors and to preserve actor references | ||
// we do it to avoid setState in useEffect when the effect gets "reconnected" | ||
// this currently only happens in Strict Effects but it simulates the Offscreen aka Activity API | ||
// it also just allows us to end up with a somewhat more predictable behavior for the users | ||
const persistedSnapshots = []; | ||
forEachActor(actorRef, ref => { | ||
persistedSnapshots.push([ref, ref.getSnapshot()]); | ||
// muting observers allow us to avoid `useSelector` from being notified about the stopped state | ||
// React reconnects its subscribers (from the useSyncExternalStore) on its own | ||
// and userland subscibers should basically always do the same anyway | ||
// as each subscription should have its own cleanup logic and that should be called each such reconnect | ||
ref.observers = new Set(); | ||
}); | ||
actorRef.stop(); | ||
persistedSnapshots.forEach(([ref, snapshot]) => { | ||
ref._processingStatus = 0; | ||
ref._state = snapshot; | ||
}); | ||
} | ||
function useIdleInterpreter(machine, options) { | ||
const actorRef = useConstant(() => { | ||
return createActor(machine, options); | ||
function useIdleActor(logic, options) { | ||
let [[currentConfig, actorRef], setCurrent] = useState(() => { | ||
const actorRef = createActor(logic, options); | ||
return [logic.config, actorRef]; | ||
}); | ||
if (logic.config !== currentConfig) { | ||
const newActorRef = createActor(logic, { | ||
...options, | ||
state: actorRef.getPersistedState({ | ||
__unsafeAllowInlineActors: true | ||
}) | ||
}); | ||
setCurrent([logic.config, newActorRef]); | ||
actorRef = newActorRef; | ||
} | ||
// TODO: consider using `useAsapEffect` that would do this in `useInsertionEffect` is that's available | ||
useIsomorphicLayoutEffect(() => { | ||
actorRef.logic.implementations = machine.implementations; | ||
actorRef.logic.implementations = logic.implementations; | ||
}); | ||
return actorRef; | ||
} | ||
function useActorRef(machine, ...[options = {}, observerOrListener]) { | ||
const actorRef = useIdleInterpreter(machine, options); | ||
function useActorRef(machine, options = {}, observerOrListener) { | ||
const actorRef = useIdleActor(machine, options); | ||
useEffect(() => { | ||
@@ -43,7 +74,5 @@ if (!observerOrListener) { | ||
return () => { | ||
actorRef.stop(); | ||
actorRef.status = ActorStatus.NotStarted; | ||
actorRef._initState(); | ||
stopRootWithRehydration(actorRef); | ||
}; | ||
}, []); | ||
}, [actorRef]); | ||
return actorRef; | ||
@@ -53,3 +82,3 @@ } | ||
function useActor(logic, options = {}) { | ||
const actorRef = useIdleInterpreter(logic, options); | ||
const actorRef = useIdleActor(logic, options); | ||
const getSnapshot = useCallback(() => { | ||
@@ -68,5 +97,3 @@ return actorRef.getSnapshot(); | ||
return () => { | ||
actorRef.stop(); | ||
actorRef.status = ActorStatus.NotStarted; | ||
actorRef._initState(); | ||
stopRootWithRehydration(actorRef); | ||
}; | ||
@@ -73,0 +100,0 @@ }, [actorRef]); |
{ | ||
"name": "@xstate/react", | ||
"version": "4.0.0-beta.10", | ||
"version": "4.0.0-beta.11", | ||
"description": "XState tools for React", | ||
@@ -58,3 +58,3 @@ "keywords": [ | ||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0", | ||
"xstate": "^5.0.0-beta.29" | ||
"xstate": "^5.0.0-beta.41" | ||
}, | ||
@@ -80,4 +80,4 @@ "peerDependenciesMeta": { | ||
"react-dom": "^18.0.0", | ||
"xstate": "5.0.0-beta.29" | ||
"xstate": "5.0.0-beta.41" | ||
} | ||
} |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
35666
827