CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-xstate--react

React hooks and utilities for integrating XState finite state machines and statecharts into React applications

Overview
Eval results
Files

core-hooks.mddocs/

Core Hooks

The core hooks provide the primary interface for integrating XState actors with React components. These hooks manage actor lifecycle and state synchronization automatically.

useActor

Creates and manages an XState actor with automatic React state synchronization. Returns the current snapshot, send function, and actor reference.

function useActor<TLogic extends AnyActorLogic>(
  logic: TLogic,
  options?: ActorOptions<TLogic> & {
    [K in RequiredActorOptionsKeys<TLogic>]: unknown;
  }
): [SnapshotFrom<TLogic>, Actor<TLogic>['send'], Actor<TLogic>];

Parameters

  • logic: The XState actor logic (machine, promise, observable, etc.)
  • options: Optional actor configuration options

Returns

A tuple containing:

  • snapshot: Current actor snapshot/state
  • send: Function to send events to the actor
  • actorRef: The actor reference for direct access

Usage Example

import { useActor } from "@xstate/react";
import { createMachine, assign } from "xstate";

const counterMachine = createMachine({
  id: "counter",
  initial: "idle",
  context: { count: 0 },
  states: {
    idle: {
      on: {
        INCREMENT: {
          actions: assign({ count: ({ context }) => context.count + 1 })
        }
      }
    }
  }
});

function Counter() {
  const [state, send, actorRef] = useActor(counterMachine);

  return (
    <div>
      <p>Count: {state.context.count}</p>
      <button onClick={() => send({ type: "INCREMENT" })}>
        Increment
      </button>
    </div>
  );
}

Features

  • Automatic actor lifecycle management (start/stop)
  • Optimized re-rendering using useSyncExternalStore
  • Development-time validation to prevent common mistakes
  • SSR compatibility with isomorphic layout effects

useActorRef

Creates an actor reference without automatic state subscription. Useful when you need the actor reference but don't want automatic re-renders.

function useActorRef<TLogic extends AnyActorLogic>(
  machine: TLogic,
  options?: ActorOptions<TLogic>,
  observerOrListener?: Observer<SnapshotFrom<TLogic>> | ((value: SnapshotFrom<TLogic>) => void)
): Actor<TLogic>;

Parameters

  • machine: The XState actor logic
  • options: Optional actor configuration options
  • observerOrListener: Optional observer or listener function for state changes

Returns

The actor reference for sending events and accessing state.

Usage Example

import { useActorRef, useSelector } from "@xstate/react";
import { createMachine } from "xstate";

const timerMachine = createMachine({
  id: "timer",
  initial: "idle",
  context: { elapsed: 0 },
  states: {
    idle: {
      on: { START: "running" }
    },
    running: {
      on: { STOP: "idle" }
    }
  }
});

function Timer() {
  const actorRef = useActorRef(timerMachine);
  const elapsed = useSelector(actorRef, (state) => state.context.elapsed);
  const isRunning = useSelector(actorRef, (state) => state.matches("running"));

  return (
    <div>
      <p>Elapsed: {elapsed}ms</p>
      <button onClick={() => actorRef.send({ type: isRunning ? "STOP" : "START" })}>
        {isRunning ? "Stop" : "Start"}
      </button>
    </div>
  );
}

Features

  • Manual control over state subscription
  • Optional observer for custom state change handling
  • Actor reference persists across re-renders
  • Supports snapshot rehydration for React Strict Mode

useSelector

Subscribes to a specific part of an actor's state using a selector function. Optimizes re-rendering by only updating when the selected value changes.

function useSelector<
  TActor extends Pick<AnyActorRef, 'subscribe' | 'getSnapshot'> | undefined,
  T
>(
  actor: TActor,
  selector: (
    snapshot: TActor extends { getSnapshot(): infer TSnapshot } ? TSnapshot : undefined
  ) => T,
  compare?: (a: T, b: T) => boolean
): T;

Parameters

  • actor: The actor reference to subscribe to (can be undefined)
  • selector: Function to extract the desired value from the snapshot
  • compare: Optional comparison function (defaults to ===)

Returns

The selected value from the actor's current snapshot.

Usage Example

import { useActorRef, useSelector } from "@xstate/react";
import { createMachine } from "xstate";

const appMachine = createMachine({
  id: "app",
  initial: "loading",
  context: { 
    user: null, 
    posts: [],
    error: null 
  },
  states: {
    loading: {
      on: { LOADED: "idle" }
    },
    idle: {
      on: { ERROR: "error" }
    },
    error: {}
  }
});

function UserProfile() {
  const appActor = useActorRef(appMachine);
  
  // Only re-render when user changes
  const user = useSelector(appActor, (state) => state.context.user);
  
  // Only re-render when loading state changes
  const isLoading = useSelector(appActor, (state) => state.matches("loading"));
  
  // Custom comparison for complex objects
  const posts = useSelector(
    appActor, 
    (state) => state.context.posts,
    (a, b) => a.length === b.length && a.every((post, i) => post.id === b[i].id)
  );

  if (isLoading) return <div>Loading...</div>;

  return (
    <div>
      <h1>{user?.name}</h1>
      <p>{posts.length} posts</p>
    </div>
  );
}

Features

  • Efficient re-rendering with custom comparison functions
  • Works with any actor reference or undefined values
  • Built on React's useSyncExternalStoreWithSelector
  • Supports complex state selection patterns

Common Patterns

Conditional Actor Usage

function ConditionalComponent({ shouldUseActor }: { shouldUseActor: boolean }) {
  const [state, send] = useActor(
    shouldUseActor ? myMachine : createMachine({ initial: "idle", states: { idle: {} } })
  );

  // Component logic...
}

Actor Reference Sharing

function Parent() {
  const actorRef = useActorRef(sharedMachine);

  return (
    <div>
      <Child1 actor={actorRef} />
      <Child2 actor={actorRef} />
    </div>
  );
}

function Child1({ actor }: { actor: Actor<typeof sharedMachine> }) {
  const value = useSelector(actor, (state) => state.context.someValue);
  return <div>{value}</div>;
}

Performance Optimization

// Use useSelector with custom comparison for expensive operations
const expensiveValue = useSelector(
  actor,
  (state) => computeExpensiveValue(state.context.data),
  (a, b) => a.id === b.id // Only recompute when ID changes
);

Install with Tessl CLI

npx tessl i tessl/npm-xstate--react

docs

context-utilities.md

core-hooks.md

index.md

utility-functions.md

tile.json