Skip to content

Releases: statelyai/xstate

[email protected]

11 Oct 04:51
cf9bbee
Compare
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/[email protected]

11 Oct 04:51
cf9bbee
Compare
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/[email protected]

03 Oct 22:28
cd9af19
Compare
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.

[email protected]

02 Oct 17:41
c9c726d
Compare
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

[email protected]

18 Sep 20:08
b40b20d
Compare
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]
    });

[email protected]

30 Aug 14:33
b413088
Compare
Choose a tag to compare

Minor Changes

  • #5364 15e15b5 Thanks @davidkpiano! - Added .createStateConfig(…) to the setup API. This makes it possible to create state configs that are strongly typed and modular.

    const lightMachineSetup = setup({
      // ...
    });
    
    const green = lightMachineSetup.createStateConfig({
      //...
    });
    
    const yellow = lightMachineSetup.createStateConfig({
      //...
    });
    
    const red = lightMachineSetup.createStateConfig({
      //...
    });
    
    const machine = lightMachineSetup.createMachine({
      initial: 'green',
      states: {
        green,
        yellow,
        red
      }
    });

@xstate/[email protected]

22 Aug 00:15
7dddf17
Compare
Choose a tag to compare

Patch Changes

@xstate/[email protected]

20 Aug 20:40
cd66f51
Compare
Choose a tag to compare

Patch Changes

  • #5359 3f4bffc Thanks @davidkpiano! - Fix createStoreHook to create a single shared store instance across all components. Previously, the implementation was creating independent store instances, but now multiple components using the same hook will share state as expected.

@xstate/[email protected]

19 Aug 13:16
c5a14bc
Compare
Choose a tag to compare

Minor Changes

  • #5354 515cc31 Thanks @davidkpiano! - Add createStoreHook(…) function for React. Creates a store hook that returns [selectedValue, store] instead of managing store instances manually.

    const useCountStore = createStoreHook({
      context: { count: 0 },
      on: {
        inc: (ctx, event: { by: number }) => ({
          ...ctx,
          count: ctx.count + event.by
        })
      }
    });
    
    // Usage
    const [count, store] = useCountStore((s) => s.context.count);
    store.trigger.inc({ by: 3 });
    
    // Usage (no selector)
    const [snapshot, store] = useCountStore();

[email protected]

06 Aug 11:47
0002811
Compare
Choose a tag to compare

Patch Changes

  • #5351 71387ff Thanks @davidkpiano! - Fix: Emit callback errors no longer crash the actor

    actor.on('event', () => {
      // Will no longer crash the actor
      throw new Error('oops');
    });