Skip to content

Subscriptions

Graphlet allows you to subscribe to graph changes.

Subscriptions are framework-agnostic. They work without React, Svelte, Vue or any UI library.

React users usually do not need to call subscribe() directly because the React adapter provides useGraph(graph), but subscriptions are the low-level mechanism behind it.

Basic usage

ts
import { Graph } from "@graphlet/core";

const graph = new Graph(
  [
    ["step1", ["step2"]],
    ["step2", []]
  ] as const,
  {
    initial: "step1"
  }
);

const unsubscribe = graph.subscribe((snapshot, event) => {
  console.log(snapshot);
  console.log(event);
});

The listener receives two arguments:

ts
(snapshot, event) => void

Snapshot

The snapshot describes the current graph state.

ts
{
  current: "step1",
  next: ["step2"],
  context: undefined,
  history: ["step1"]
}

Snapshot includes:

ts
{
  current: Node;
  next: Node[];
  context: Context;
  history: Node[];
}

Event

The event describes why the subscription was triggered.

Graphlet currently emits these event types:

ts
"init"
"transition"
"context"
"back"
"history.clear"
"reset"

Init event

subscribe() calls the listener immediately with an init event.

ts
{
  type: "init",
  current: "step1"
}

This makes subscriptions convenient for UI adapters because they can render the initial state immediately.

Transition event

A successful goTo() emits a transition event.

ts
graph.goTo("step2", {
  source: "button"
});

Subscriber receives:

ts
{
  type: "transition",
  from: "step1",
  to: "step2",
  payload: {
    source: "button"
  }
}

The snapshot already contains the new current node.

ts
{
  current: "step2",
  next: [],
  context: undefined,
  history: ["step1", "step2"]
}

Failed transitions do not notify subscribers.

Context event

setContext() emits a context event.

ts
graph.setContext((ctx) => ({
  ...ctx,
  completed: ["step1"]
}));

Subscriber receives:

ts
{
  type: "context",
  current: "step1",
  previousContext: {
    completed: []
  },
  context: {
    completed: ["step1"]
  }
}

Back event

back() emits a back event when it succeeds.

ts
graph.back();

Subscriber receives:

ts
{
  type: "back",
  from: "step2",
  to: "step1"
}

If history is empty, back() returns a failure result and does not notify subscribers.

History clear event

clearHistory() emits a history.clear event.

ts
graph.clearHistory();

Subscriber receives:

ts
{
  type: "history.clear",
  current: "step2"
}

Reset event

reset() emits a reset event.

ts
graph.reset();

Subscriber receives:

ts
{
  type: "reset",
  from: "step2",
  to: "step1"
}

The snapshot contains the initial node and reset history.

ts
{
  current: "step1",
  next: ["step2"],
  context: undefined,
  history: ["step1"]
}

Unsubscribe

subscribe() returns an unsubscribe function.

ts
const unsubscribe = graph.subscribe((snapshot, event) => {
  console.log(snapshot, event);
});

unsubscribe();

After calling unsubscribe, the listener will no longer be called.

React usage

In React, prefer useGraph(graph).

tsx
import { useGraph } from "@graphlet/react";

function DebugPanel({ graph }) {
  const { snapshot, event } = useGraph(graph);

  return (
    <pre>
      {JSON.stringify({ snapshot, event }, null, 2)}
    </pre>
  );
}

Use low-level subscribe() directly only when you need custom integrations.