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

context-utilities.mddocs/

Context Utilities

The context utilities provide a way to share XState actors across React component trees using React Context. This is ideal for global state management and avoiding prop drilling.

createActorContext

Creates a React context with provider and context-bound hooks for sharing an XState actor across components.

function createActorContext<TLogic extends AnyActorLogic>(
  actorLogic: TLogic,
  actorOptions?: ActorOptions<TLogic>
): {
  useSelector: <T>(
    selector: (snapshot: SnapshotFrom<TLogic>) => T,
    compare?: (a: T, b: T) => boolean
  ) => T;
  useActorRef: () => Actor<TLogic>;
  Provider: React.ComponentType<{
    children: React.ReactNode;
    options?: ActorOptions<TLogic>;
    logic?: TLogic;
  }>;
};

Parameters

  • actorLogic: The XState actor logic to be shared
  • actorOptions: Default actor configuration options

Returns

An object containing:

  • useSelector: Context-bound selector hook
  • useActorRef: Context-bound actor reference hook
  • Provider: React component to provide the actor context

Usage Example

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

// Define your machine
const authMachine = createMachine({
  id: "auth",
  initial: "loggedOut",
  context: {
    user: null,
    token: null
  },
  states: {
    loggedOut: {
      on: {
        LOGIN: {
          target: "loggedIn",
          actions: assign({
            user: ({ event }) => event.user,
            token: ({ event }) => event.token
          })
        }
      }
    },
    loggedIn: {
      on: {
        LOGOUT: {
          target: "loggedOut",
          actions: assign({
            user: null,
            token: null
          })
        }
      }
    }
  }
});

// Create the context
const AuthContext = createActorContext(authMachine);

// App component with provider
function App() {
  return (
    <AuthContext.Provider>
      <Navigation />
      <MainContent />
    </AuthContext.Provider>
  );
}

// Components that use the context
function Navigation() {
  const user = AuthContext.useSelector((state) => state.context.user);
  const isLoggedIn = AuthContext.useSelector((state) => state.matches("loggedIn"));
  const authActor = AuthContext.useActorRef();

  if (!isLoggedIn) {
    return (
      <nav>
        <button onClick={() => authActor.send({ 
          type: "LOGIN", 
          user: { name: "John" }, 
          token: "abc123" 
        })}>
          Login
        </button>
      </nav>
    );
  }

  return (
    <nav>
      <span>Welcome, {user.name}!</span>
      <button onClick={() => authActor.send({ type: "LOGOUT" })}>
        Logout
      </button>
    </nav>
  );
}

function MainContent() {
  const isLoggedIn = AuthContext.useSelector((state) => state.matches("loggedIn"));
  
  return (
    <main>
      {isLoggedIn ? <Dashboard /> : <LoginPrompt />}
    </main>
  );
}

Provider Component

The Provider component created by createActorContext accepts the following props:

interface ProviderProps<TLogic extends AnyActorLogic> {
  children: React.ReactNode;
  options?: ActorOptions<TLogic>;
  logic?: TLogic;
  /** @deprecated Use `logic` instead. */
  machine?: never;
}

Props

  • children: React children to receive the context
  • options: Optional actor configuration options (overrides defaults)
  • logic: Optional different actor logic (overrides default)

Usage with Custom Options

function App() {
  return (
    <AuthContext.Provider 
      options={{ 
        input: { apiUrl: "https://api.example.com" }
      }}
    >
      <AppContent />
    </AuthContext.Provider>
  );
}

Usage with Different Logic

const devAuthMachine = createMachine({
  // Development version with mock data
});

function App() {
  const isDevelopment = process.env.NODE_ENV === "development";
  
  return (
    <AuthContext.Provider 
      logic={isDevelopment ? devAuthMachine : authMachine}
    >
      <AppContent />
    </AuthContext.Provider>
  );
}

Context-bound Hooks

useSelector

The context-bound useSelector hook works identically to the standalone version but automatically uses the actor from context.

const contextUseSelector: <T>(
  selector: (snapshot: SnapshotFrom<TLogic>) => T,
  compare?: (a: T, b: T) => boolean
) => T;

useActorRef

The context-bound useActorRef hook returns the actor reference from context.

const contextUseActorRef: () => Actor<TLogic>;

Error Handling

The context hooks will throw an error if used outside of their corresponding Provider:

function ComponentOutsideProvider() {
  // This will throw an error
  const user = AuthContext.useSelector((state) => state.context.user);
  // Error: You used a hook from "ActorProvider" but it's not inside a <ActorProvider> component.
}

Multiple Contexts

You can create and use multiple actor contexts in the same application:

const AuthContext = createActorContext(authMachine);
const ThemeContext = createActorContext(themeMachine);
const CartContext = createActorContext(cartMachine);

function App() {
  return (
    <AuthContext.Provider>
      <ThemeContext.Provider>
        <CartContext.Provider>
          <AppContent />
        </CartContext.Provider>
      </ThemeContext.Provider>
    </AuthContext.Provider>
  );
}

function AppContent() {
  const user = AuthContext.useSelector((state) => state.context.user);
  const theme = ThemeContext.useSelector((state) => state.context.theme);
  const cartItems = CartContext.useSelector((state) => state.context.items);
  
  // Use all three contexts...
}

Common Patterns

Combining with Custom Hooks

// Custom hook for auth operations
function useAuth() {
  const user = AuthContext.useSelector((state) => state.context.user);
  const isLoggedIn = AuthContext.useSelector((state) => state.matches("loggedIn"));
  const authActor = AuthContext.useActorRef();

  const login = useCallback((credentials) => {
    authActor.send({ type: "LOGIN", ...credentials });
  }, [authActor]);

  const logout = useCallback(() => {
    authActor.send({ type: "LOGOUT" });
  }, [authActor]);

  return { user, isLoggedIn, login, logout };
}

Performance Optimization

// Memoize selectors for complex computations
const memoizedSelector = useMemo(
  () => (state) => expensiveComputation(state.context.data),
  []
);

const result = AuthContext.useSelector(memoizedSelector);

Conditional Context Usage

function FeatureComponent({ useGlobalState }: { useGlobalState: boolean }) {
  if (useGlobalState) {
    const data = GlobalContext.useSelector((state) => state.context.data);
    return <div>{data}</div>;
  }
  
  // Use local state instead
  const [localData] = useState("local");
  return <div>{localData}</div>;
}

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