Releases: statelyai/xstate
@xstate/graph@2.0.0
Major Changes
-
#4896
7c6e2ea
Thanks @davidkpiano! - Test model path generation now has the option to allow duplicate paths by settingallowDuplicatePaths: true
:const paths = model.getSimplePaths({ allowDuplicatePaths: true }); // a // a -> b // a -> b -> c // a -> d // a -> d -> e
By default,
allowDuplicatePaths
is set tofalse
:const paths = model.getSimplePaths(); // a -> b -> c // a -> d -> e
-
#4896
7c6e2ea
Thanks @davidkpiano! - TheadjacencyMapToArray(…)
helper function has been introduced, which converts an adjacency map to an array of{ state, event, nextState }
objects.import { getAdjacencyMap, adjacencyMapToArray } from '@xstate/graph'; const machine = createMachine({ initial: 'green', states: { green: { on: { TIMER: 'yellow' } }, yellow: { on: { TIMER: 'red' } }, red: { on: { TIMER: 'green' } } } }); const arr = adjacencyMapToArray(getAdjacencyMap(machine)); // [ // { // "state": {value: "green", ... }, // "event": { type: "TIMER" }, // "nextState": { value: "yellow", ... } // }, // { // "state": {value: "yellow", ... }, // "event": { type: "TIMER" }, // "nextState": { value: "red", ... } // }, // { // "state": {value: "red", ... }, // "event": { type: "TIMER" }, // "nextState": { value: "green", ... } // }, // { // "state": {value: "green", ... }, // "event": { type: "TIMER" }, // "nextState": { value: "yellow", ... } // }, // ]
-
#4896
7c6e2ea
Thanks @davidkpiano! - ThetraversalLimit
option has been renamed tolimit
:model.getShortestPaths({ - traversalLimit: 100 + limit: 100 });
-
#4233
3d96d0f95
Thanks @davidkpiano! - RemovegetMachineShortestPaths
andgetMachineSimplePaths
import { - getMachineShortestPaths, + getShortestPaths, - getMachineSimplePaths, + getSimplePaths } from '@xstate/graph'; -const paths = getMachineShortestPaths(machine); +const paths = getShortestPaths(machine); -const paths = getMachineSimplePaths(machine); +const paths = getSimplePaths(machine);
-
#4238
b4f12a517
Thanks @davidkpiano! - The steps in the paths returned from functions likegetShortestPaths(...)
andgetSimplePaths(...)
have the following changes:- The
step.event
property now represents theevent
object that resulted in the transition to thestep.state
, not the event that comes before the next step. - The
path.steps
array now includes the targetpath.state
as the last step.- Note: this means that
path.steps
always has at least one step.
- Note: this means that
- The first
step
now has the{ type: 'xstate.init' }
event
- The
-
#4896
7c6e2ea
Thanks @davidkpiano! - ThecreateTestMachine(…)
function has been removed. Use a normalcreateMachine(…)
orsetup(…).createMachine(…)
function instead to create machines for path generation. -
#4896
7c6e2ea
Thanks @davidkpiano! - Thefilter
andstopCondition
option for path generation has been renamed tostopWhen
, which is used to stop path generation when a condition is met. This is a breaking change, but it is a more accurate name for the option.const shortestPaths = getShortestPaths(machine, { events: [{ type: 'INC' }], - filter: (state) => state.context.count < 5 - stopCondition: (state) => state.context.count < 5 + stopWhen: (state) => state.context.count === 5 });
-
#4896
7c6e2ea
Thanks @davidkpiano! - Path generation now supportsinput
for actor logic:const model = createTestModel( setup({ types: { input: {} as { name: string; }, context: {} as { name: string; } } }).createMachine({ context: (x) => ({ name: x.input.name }), initial: 'checking', states: { checking: { always: [ { guard: (x) => x.context.name.length > 3, target: 'longName' }, { target: 'shortName' } ] }, longName: {}, shortName: {} } }) ); const path1 = model.getShortestPaths({ input: { name: 'ed' } }); expect(path1[0].steps.map((s) => s.state.value)).toEqual(['shortName']); const path2 = model.getShortestPaths({ input: { name: 'edward' } }); expect(path2[0].steps.map((s) => s.state.value)).toEqual(['longName']);
-
#4896
7c6e2ea
Thanks @davidkpiano! - The test model "sync" methods have been removed, including:testModel.testPathSync(…)
testModel.testStateSync(…)
testPath.testSync(…)
The
async
methods should always be used instead.model.getShortestPaths().forEach(async (path) => { - model.testPathSync(path, { + await model.testPath(path, { states: { /* ... */ }, events: { /* ... */ }, }); })
Minor Changes
- #3727
5fb3c683d
Thanks @Andarist! -exports
field has been added to thepackage.json
manifest. It limits what files can be imported from a package - it's no longer possible to import from files that are not considered to be a part of the public API.
Patch Changes
-
#4896
7c6e2ea
Thanks @davidkpiano! - The@xstate/graph
package now includes everything from@xstate/test
. -
#4308
af032db12
Thanks @davidkpiano! - Traversing state machines that have delayed transitions will now work as expected:const machine = createMachine({ initial: 'a', states: { a: { after: { 1000: 'b' } }, b: {} } }); const paths = getShortestPaths(machine); // works
@xstate/store@0.0.4
Patch Changes
-
#4890
6d92b7770
Thanks @davidkpiano! - Thecontext
type forcreateStoreWithProducer(producer, context, transitions)
will now be properly inferred.const store = createStoreWithProducer( produce, { count: 0 }, { // ... } ); store.getSnapshot().context; // BEFORE: StoreContext // NOW: { count: number }
xstate@5.13.0
Minor Changes
-
#4832
148d8fcef
Thanks @cevr! -fromPromise
now passes a signal into its creator function.const logic = fromPromise(({ signal }) => fetch('https://api.example.com', { signal }) );
This will be called whenever the state transitions before the promise is resolved. This is useful for cancelling the promise if the state changes.
Patch Changes
-
#4876
3f6a73b56
Thanks @davidkpiano! - XState will now warn when calling built-in actions likeassign
,sendTo
,raise
,emit
, etc. directly inside of a custom action. See https://stately.ai/docs/actions#built-in-actions for more details.const machine = createMachine({ entry: () => { // Will warn: // "Custom actions should not call \`assign()\` directly, as it is not imperative. See https://stately.ai/docs/actions#built-in-actions for more details." assign({ // ... }); } });
xstate@5.12.0
Minor Changes
-
#4863
0696adc21
Thanks @davidkpiano! - Meta objects for state nodes and transitions can now be specified insetup({ types: … })
:const machine = setup({ types: { meta: {} as { layout: string; } } }).createMachine({ initial: 'home', states: { home: { meta: { layout: 'full' } } } }); const actor = createActor(machine).start(); actor.getSnapshot().getMeta().home; // => { layout: 'full' } // if in "home" state
@xstate/vue@3.1.2
Patch Changes
-
#4844
5aa6eb05c
Thanks @davidkpiano! - TheuseSelector(…)
hook from@xstate/react
is now compatible with stores from@xstate/store
.import { createStore } from '@xstate/store'; import { useSelector } from '@xstate/react'; const store = createStore( { count: 0 }, { inc: { count: (context) => context.count + 1 } } ); function Counter() { // Note that this `useSelector` is from `@xstate/react`, // not `@xstate/store/react` const count = useSelector(store, (state) => state.context.count); return ( <div> <button onClick={() => store.send({ type: 'inc' })}>{count}</button> </div> ); }
@xstate/svelte@3.0.3
Patch Changes
-
#4844
5aa6eb05c
Thanks @davidkpiano! - TheuseSelector(…)
hook from@xstate/react
is now compatible with stores from@xstate/store
.import { createStore } from '@xstate/store'; import { useSelector } from '@xstate/react'; const store = createStore( { count: 0 }, { inc: { count: (context) => context.count + 1 } } ); function Counter() { // Note that this `useSelector` is from `@xstate/react`, // not `@xstate/store/react` const count = useSelector(store, (state) => state.context.count); return ( <div> <button onClick={() => store.send({ type: 'inc' })}>{count}</button> </div> ); }
@xstate/react@4.1.1
Patch Changes
-
#4844
5aa6eb05c
Thanks @davidkpiano! - TheuseSelector(…)
hook from@xstate/react
is now compatible with stores from@xstate/store
.import { createStore } from '@xstate/store'; import { useSelector } from '@xstate/react'; const store = createStore( { count: 0 }, { inc: { count: (context) => context.count + 1 } } ); function Counter() { // Note that this `useSelector` is from `@xstate/react`, // not `@xstate/store/react` const count = useSelector(store, (state) => state.context.count); return ( <div> <button onClick={() => store.send({ type: 'inc' })}>{count}</button> </div> ); }
xstate@5.11.0
Minor Changes
-
#4806
f4e0ec48c
Thanks @davidkpiano! - Inline actor logic is now permitted when named actors are present. Defining inline actors will no longer cause a TypeScript error:const machine = setup({ actors: { existingActor: fromPromise(async () => { // ... }) } }).createMachine({ invoke: { src: fromPromise(async () => { // Inline actor }) // ... } });
xstate@5.10.0
Minor Changes
-
#4822
f7f1fbbf3
Thanks @davidkpiano! - Theclock
andlogger
specified in theoptions
object ofcreateActor(logic, options)
will now propagate to all actors created within the same actor system.import { setup, log, createActor } from 'xstate'; const childMachine = setup({ // ... }).createMachine({ // ... // Uses custom logger from root actor entry: log('something') }); const parentMachine = setup({ // ... }).createMachine({ // ... invoke: { src: childMachine } }); const actor = createActor(parentMachine, { logger: (...args) => { // custom logger for args } }); actor.start();
@xstate/store@0.0.3
Patch Changes
-
#4842
3a57f4c69
Thanks @davidkpiano! - Update README.md -
#4839
4a22edb90
Thanks @davidkpiano! - Update JS docs