8000 Releases · statelyai/xstate · GitHub
[go: up one dir, main page]

Skip to content

Releases: statelyai/xstate

@xstate/store@3.12.0

24 Nov 15:52
7cf65dd

Choose a tag to compare

Minor Changes

  • #5410 45d97de Thanks @davidkpiano! - Fix go-to-definition for triggers

  • #5414 524a207 Thanks @davidkpiano! - Computed atoms can now access their previous value via an optional second parameter:

    const count = createAtom(1);
    const double = createAtom<number>((_, prev) => count.get() + (prev ?? 0));

xstate@5.24.0

03 Nov 18:53
462e0b2

Choose a tag to compare

Minor Changes

  • #5371 b8ec3b1 Thanks @davidkpiano! - Add setup.extend() method to incrementally extend machine setup configurations with additional actions, guards, and delays. This enables composable and reusable machine setups where extended actions, guards, and delays can reference base actions, guards, and delays and support chaining multiple extensions:

    import { setup, not, and } from 'xstate';
    
    const baseSetup = setup({
      guards: {
        isAuthenticated: () => true,
        hasPermission: () => false
      }
    });
    
    const extendedSetup = baseSetup.extend({
      guards: {
        // Type-safe guard references
        isUnauthenticated: not('isAuthenticated'),
        canAccess: and(['isAuthenticated', 'hasPermission'])
      }
    });
    
    // Both base and extended guards are available
    extendedSetup.createMachine({
      on: {
        LOGIN: {
          guard: 'isAuthenticated',
          target: 'authenticated'
        },
        LOGOUT: {
          guard: 'isUnauthenticated',
          target: 'unauthenticated'
        }
      }
    });

@xstate/store@3.11.2

27 Oct 13:27
e388c70

Choose a tag to compare

Patch Changes

@xstate/store@3.11.1

17 Oct 20:12
99b9047

Choose a tag to compare

Patch Changes

  • #5395 8408430 Thanks @davidkpiano! - Fix redo logic bug where redo would apply too many events when no transaction grouping is used

@xstate/store@3.11.0

17 Oct 15:10
3bbfad3

Choose a tag to compare

Minor Changes

  • #5393 6d00d3f Thanks @davidkpiano! - Add snapshot parameter to getTransactionId function.

    const store = createStore(
      undo(
        {
          // ...
        },
        {
          getTransactionId: (event, snapshot) =>
            snapshot.context.currentTransactionId
        }
      )
    );
  • #5392 5854b52 Thanks @davidkpiano! - Added an overload to useSelector that allows you to select the entire snapshot:

    // No selector provided, return the entire snapshot
    const snapshot = useSelector(store);
  • #5393 6d00d3f Thanks @davidkpiano! - Add skipEvent option to undoRedo() to exclude certain events from undo/redo history.

    const store = createStore(
      undoRedo(
        {
          context: { count: 0 },
          on: {
            inc: (ctx) => ({ count: ctx.count + 1 }),
            log: (ctx) => ctx // No state change
          }
        },
        {
          skipEvent: (event, snapshot) => event.type === 'log'
        }
      )
    );

xstate@5.23.0

11 Oct 04:51
cf9bbee

Choose a tag to compare

Minor Changes

  • #5387 53dd7f1 Thanks @farskid! - Adds system.getAll that returns a record of running actors within the system by their system id

    const childMachine = createMachine({});
    const machine = createMachine({
      // ...
      invoke: [
        {
          src: childMachine,
          systemId: 'test'
        }
      ]
    });
    const system = createActor(machine);
    
    system.getAll(); // { test: ActorRefFrom<typeof childMachine> }

@xstate/store@3.10.0

11 Oct 04:51
cf9bbee

Choose a tag to compare

Minor Changes

  • #5323 cb08332 Thanks @davidkpiano! - Added support for effect-only transitions that don't trigger state updates. Now, when a transition returns the same state but includes effects, subscribers won't be notified of a state change, but the effects will still be executed. This helps prevent unnecessary re-renders while maintaining side effect functionality.

    it('should not trigger update if the snapshot is the same even if there are effects', () => {
      const store = createStore({
        context: { count: 0 },
        on: {
          doNothing: (ctx, _, enq) => {
            enq.effect(() => {
              // …
            });
            return ctx; // Context is the same, so no update is triggered
            // This is the same as not returning anything (void)
          }
        }
      });
    
      const spy = vi.fn();
      store.subscribe(spy);
    
      store.trigger.doNothing();
      store.trigger.doNothing();
    
      expect(spy).toHaveBeenCalledTimes(0);
    });

@xstate/store@3.9.3

03 Oct 22:28
cd9af19

Choose a tag to compare

Patch Changes

  • #5383 4b6a513 Thanks @davidkpiano! - Fix: trigger methods now work when passed directly as event handlers, even for events with no payload. Before, the React event.type would overwrite the intended event type.

xstate@5.22.1

02 Oct 17:41
c9c726d

Choose a tag to compare

Patch Changes

  • #5379 98f9ddd Thanks @davidkpiano! - Make actor.systemId public:

    const actor = createActor(machine, { systemId: 'test' });
    actor.systemId; // 'test'
  • #5380 e7e5e44 Thanks @Nirajkashyap! - fix: remove 'eventType' from required fields in initialTransitionObject

xstate@5.22.0

18 Sep 20:08
b40b20d

Choose a tag to compare

Minor Changes

  • #5367 76c857e Thanks @davidkpiano! - Add type-bound action helpers to setup():

    • createAction(fn) – create type-safe custom actions
    • setup().assign(...), setup().sendTo(...), setup().raise(...), setup().log(...), setup().cancel(...), setup().stopChild(...), setup().enqueueActions(...), setup().emit(...), setup().spawnChild(...) – setup-scoped helpers that are fully typed to the setup's context/events/actors/guards/delays/emitted.

    These helpers return actions that are bound to the specific setup() they were created from and can be used directly in the machine produced by that setup.

    const machineSetup = setup({
      types: {} as {
        context: {
          count: number;
        };
        events: { type: 'inc'; value: number } | { type: 'TEST' };
        emitted: { type: 'PING' };
      }
    });
    
    // Custom action
    const action = machineSetup.createAction(({ context, event }) => {
      console.log(context.count, event.value);
    });
    
    // Type-bound built-ins (no wrapper needed)
    const increment = machineSetup.assign({
      count: ({ context }) => context.count + 1
    });
    const raiseTest = machineSetup.raise({ type: 'TEST' });
    const ping = machineSetup.emit({ type: 'PING' });
    const batch = machineSetup.enqueueActions(({ enqueue, check }) => {
      if (check(() => true)) {
        enqueue(increment);
      }
    });
    
    const machine = machineSetup.createMachine({
      context: { count: 0 },
      entry: [action, increment, raiseTest, ping, batch]
    });
0