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

utility-functions.mddocs/

Utility Functions

Utility functions and compatibility helpers provided by @xstate/react.

shallowEqual

A shallow equality comparison utility function, useful for custom comparison in selectors.

function shallowEqual(objA: any, objB: any): boolean;

Parameters

  • objA: First object to compare
  • objB: Second object to compare

Returns

true if the objects are shallowly equal, false otherwise.

Usage Example

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

function UserComponent({ actor }: { actor: Actor<any> }) {
  // Use shallow equality for object comparisons
  const userProfile = useSelector(
    actor,
    (state) => ({
      name: state.context.user.name,
      email: state.context.user.email,
      avatar: state.context.user.avatar
    }),
    shallowEqual // Prevents re-renders when object content is the same
  );

  return (
    <div>
      <h1>{userProfile.name}</h1>
      <p>{userProfile.email}</p>
      <img src={userProfile.avatar} alt="Avatar" />
    </div>
  );
}

Use Cases

  • Custom comparison in useSelector for object values
  • Preventing unnecessary re-renders when object structure is stable
  • Compatible with Redux patterns (based on react-redux implementation)

Implementation Details

The function performs the following checks:

  1. Reference equality: Returns true if both objects are the same reference
  2. Null/undefined handling: Returns false if either object is null/undefined while the other isn't
  3. Type checking: Returns false if objects are different types
  4. Key comparison: Compares all enumerable properties shallowly
  5. Property count: Returns false if objects have different numbers of properties

Alternative Comparison Functions

// Custom deep equality (for nested objects)
const deepEqual = (a, b) => JSON.stringify(a) === JSON.stringify(b);

// Custom comparison for arrays
const arrayEqual = (a, b) => 
  Array.isArray(a) && Array.isArray(b) && 
  a.length === b.length && 
  a.every((item, index) => item === b[index]);

// Usage with custom comparisons
const data = useSelector(actor, selector, deepEqual);
const items = useSelector(actor, (state) => state.context.items, arrayEqual);

useMachine (Deprecated)

Legacy hook that provides the same functionality as useActor but specifically typed for state machines. This hook is deprecated and will be removed in a future version.

/** @deprecated Use useActor instead */
function useMachine<TMachine extends AnyStateMachine>(
  machine: TMachine,
  options?: ActorOptions<TMachine> & {
    [K in RequiredActorOptionsKeys<TMachine>]: unknown;
  }
): [StateFrom<TMachine>, Actor<TMachine>['send'], Actor<TMachine>];

Parameters

  • machine: The XState state machine
  • options: Optional actor configuration options

Returns

A tuple containing:

  • state: Current machine state
  • send: Function to send events
  • actorRef: The actor reference

Migration Example

// OLD - using useMachine (deprecated)
import { useMachine } from "@xstate/react";

function OldComponent() {
  const [state, send, actorRef] = useMachine(myMachine);
  return <div>{state.value}</div>;
}

// NEW - using useActor (recommended)
import { useActor } from "@xstate/react";

function NewComponent() {
  const [state, send, actorRef] = useActor(myMachine);
  return <div>{state.value}</div>;
}

Migration Guide

  1. Replace import: Change useMachine to useActor in imports
  2. Update function calls: Replace useMachine(machine, options) with useActor(machine, options)
  3. Type updates: Return types are identical, no type changes needed
  4. Functionality: Behavior is identical - this is a direct alias

Why Deprecated?

The useMachine hook was deprecated because:

  • Consistency: useActor works with all actor types (machines, promises, observables)
  • Simplification: Reduces API surface area
  • Future-proofing: Aligns with XState v5's unified actor model

Common Utility Patterns

Custom Comparison Functions

// Compare by specific properties only
const compareById = (a, b) => a?.id === b?.id;

// Compare arrays by length
const compareArrayLength = (a, b) => 
  Array.isArray(a) && Array.isArray(b) ? a.length === b.length : a === b;

// Compare objects by specific keys
const compareByKeys = (keys) => (a, b) => 
  keys.every(key => a?.[key] === b?.[key]);

// Usage examples
const user = useSelector(actor, (state) => state.context.user, compareById);
const items = useSelector(actor, (state) => state.context.items, compareArrayLength);
const settings = useSelector(
  actor, 
  (state) => state.context.settings, 
  compareByKeys(['theme', 'language'])
);

Selector Optimization

// Memoize expensive selectors
const memoizedSelector = useMemo(
  () => (state) => {
    // Expensive computation
    return state.context.data.filter(item => 
      item.tags.some(tag => tag.includes(searchTerm))
    );
  },
  [searchTerm] // Recreate selector when searchTerm changes
);

const filteredData = useSelector(actor, memoizedSelector, shallowEqual);

Error Boundary Integration

function SafeComponent({ actor }: { actor: Actor<any> }) {
  const error = useSelector(
    actor, 
    (state) => state.context.error,
    // Custom comparison to only re-render on error message changes
    (a, b) => a?.message === b?.message
  );

  if (error) {
    throw error; // Let error boundary handle it
  }

  // Normal component rendering...
}

Performance Monitoring

function PerformanceAwareComponent({ actor }: { actor: Actor<any> }) {
  const renderCount = useRef(0);
  renderCount.current++;

  const data = useSelector(
    actor,
    (state) => {
      console.log(`Selector called, render #${renderCount.current}`);
      return state.context.data;
    },
    (a, b) => {
      const isEqual = shallowEqual(a, b);
      console.log(`Comparison result: ${isEqual}`);
      return isEqual;
    }
  );

  return <div>Render #{renderCount.current}: {data.length} items</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