github statelyai/xstate v4.6.0

4 years ago

Actors

  • 🎭 Support for actors (read up on the Actor model has landed! Actors are dynamic entities that can send and receive messages from each other, spawn other actors, and only modify their own local state. This is a great model for communicating state machines. Think of it like services but dynamic (same implementation!).

Read the docs 📖 on actors.

const todoMachine = Machine({
  id: 'todo',
  // ...
});


const todosMachine = Machine({
  id: 'todos',
  context: {
    todos: []
  },
  // ...
  on: {
    'TODOS.ADD': {
      actions: assign({
        todos: (ctx, e) => [
          ...ctx.todos,
          { data: e.data, ref: spawn(todoMachine) }
        ]
      }
    },
    'TODO.COMPLETE': {
      actions: send('COMPLETE', {
        to: (ctx, e) => ctx.todos
          .find(todo => todo.id === e.id)
          .ref
      })
    }
  }
});

Observables

  • 🔵 Observables can now be invoked (or spawned):
import { interval } from 'rxjs';
import { map } from 'rxjs/operators';
// ...

const intervalMachine = Machine({
  id: 'interval',
  context: { value: 1000 },
  invoke: {
    src: (context) => interval(context.value)
      .pipe(map(n => ({ type: 'UPDATE', value: n })))
  },
  on: {
    UPDATE: { /* ... */ }
  }
  // ...
});
const service = interpret(machine).start();

// Subscribe to state transitions
const sub = service.subscribe(state => {
  console.log(state);
});

// Stop subscribing
sub.unsubscribe();
  • 💥 For executing custom actions, service.execute(...) can now be configured dynamically with custom action implementations:
const alertMachine = Machine({
  id: 'alert',
  context: { message: '' },
  states: { /* ... */ },
  on: {
    NOTIFY: { actions: 'alert' }
  }
});

const service = interpret(alertMachine, { execute: false })
  .onTransition(state => {
    // execute with custom action
    service.execute(state, {
      alert: (context) => AlertThing.showCustomAlert(context.message)
    });
  })
  .start();

service.send('NOTIFY');
// => shows custom alert
  • 🎯 The send() action creator now supports target expressions for its to option:
// string target 
send('SOME_EVENT', { to: (ctx, e) => e.id });

// reference target
send('SOME_EVENT', { to: (ctx, e) => ctx.someActorRef });
  • 💧 New action creator: pure() allows you to specify a function that returns one or more action objects. The only rule is that this action creator must be pure (hence the name) - no executed side-effects must occur in the function passed to pure():
// Assign and do some other action
actions: pure((ctx, e) => {
  return [
    assign({ foo: e.foo }),
    { type: 'anotherAction', value: ctx.bar }
  ];
});

Fixes and enhancements

  • ⚠️ Warnings are now only displayed when not in production mode.
  • 🚥 Services that are started will now not have any unintended side-effects if .start() is called again.
  • 🕳 Transitions for deep flat parallel state value now resolve as expected (thanks @Andarist for the test) #458 #465
  • ❗️ The service.state and service.initialState are now always defined on an Interpreter (service) instance, which should fix some tiny React hook issues.
  • 🔍 Destructuring { matches } from a State instance will no longer lose its this reference, thanks to @farskid #- #440
  • ❓ A service creator that returns undefined will no longer crash the service. #430 #431

Don't miss a new xstate release

NewReleases is sending notifications on new releases.