statelyai / xstate

Actor-based state management & orchestration for complex app logic.
https://stately.ai/docs
MIT License
27.21k stars 1.26k forks source link

feat: Subscribe to emitted events from an actor #5059

Closed tnortman-jabra closed 2 months ago

tnortman-jabra commented 2 months ago

I don't want to couple the actor's logic to the architecutre of my system if that makes sense.

I just want to "emit and event" from my actor, and be able to subscribe to those events, wherever I may choose I actually have trialed out this which achieves what I am describing, but it feels like it should work out of the box this way:

import { fromEventPattern } from 'rxjs';
import type { AnyActorLogic, EmittedFrom, Subscription } from 'xstate5';
import { createActor } from 'xstate5';

export function createObservableFromActorLogic<TLogic extends AnyActorLogic>(
  actorLogic: TLogic,
  options: Parameters<typeof createActor>[1],
) {
  const actor = createActor(actorLogic, options);
  const addHandler = (handler: (event: any) => void) => {
    const subscription = actor.on('*', handler);
    console.log('[observableFactory] Actor starting...');
    actor.start(); // Start the actor
    console.log('[observableFactory] Actor started...');
    return subscription;
  };

  const removeHandler: Parameters<typeof fromEventPattern>[1] = (_, subscription: Subscription) => {
    actor.stop(); // Stop the actor
    console.log('[observableFactory] actor stopped');
    subscription.unsubscribe();
  };

  return fromEventPattern<EmittedFrom<TLogic>>(addHandler, removeHandler);
}

And a usage:

const notificationHubActor = createActor(
  NotificationHubLogic.provide({
    actors: {
      // subscribe to "ping" and "pong" events
      pingPongObserver: fromEventObservable(() => createObservableFromActorLogic(pingPongLogic)),
    },
  }),
);

Link to Discord discussion