From harness-claude
Integrates XState machines with React via useMachine, useActorRef, and useSelector hooks for state-based UI rendering, event handling, and shared instances across components.
npx claudepluginhub intense-visions/harness-engineering --plugin harness-claudeThis skill uses the workspace's default tool permissions.
> Connect XState machines to React components with useMachine, useActor, and useSelector hooks
Defines XState state machines with createMachine for explicit states, transitions, context, and events. For modeling complex UI flows like forms, wizards, authentication, and workflows preventing illegal transitions.
Mandates invoking relevant skills via tools before any response in coding sessions. Covers access, priorities, and adaptations for Claude Code, Copilot CLI, Gemini CLI.
Share bugs, ideas, or general feedback.
Connect XState machines to React components with useMachine, useActor, and useSelector hooks
useMachine(machine) to create and interpret a machine instance local to a component.[state, send, actorRef] from the hook. state contains the current state; send dispatches events.state.matches('stateName') for conditional rendering — never compare state.value as a string directly (it can be an object for compound states).state.context.useActorRef in a parent and pass it down via React context. Children use useSelector to read specific state slices.useSelector with a comparison function to prevent unnecessary re-renders.// LoginForm.tsx
import { useMachine } from '@xstate/react';
import { authMachine } from './auth.machine';
function LoginForm() {
const [state, send] = useMachine(authMachine, {
// Provide service implementations
services: {
authenticateUser: async (ctx, event) => {
const res = await fetch('/api/login', {
method: 'POST',
body: JSON.stringify({ email: event.email, password: event.password }),
});
if (!res.ok) throw new Error('Invalid credentials');
return res.json();
},
},
});
if (state.matches('authenticated')) {
return <div>Welcome, {state.context.user?.name}</div>;
}
return (
<form
onSubmit={(e) => {
e.preventDefault();
const data = new FormData(e.currentTarget);
send({ type: 'LOGIN', email: data.get('email') as string, password: data.get('password') as string });
}}
>
<input name="email" type="email" />
<input name="password" type="password" />
<button disabled={state.matches('authenticating')}>
{state.matches('authenticating') ? 'Signing in...' : 'Sign in'}
</button>
{state.matches('error') && <p>{state.context.error}</p>}
</form>
);
}
// Shared machine via React context
import { createActorContext } from '@xstate/react';
import { appMachine } from './app.machine';
const AppMachineContext = createActorContext(appMachine);
function App() {
return (
<AppMachineContext.Provider>
<Header />
<Content />
</AppMachineContext.Provider>
);
}
function Header() {
const userName = AppMachineContext.useSelector(
(state) => state.context.user?.name
);
return <header>{userName ?? 'Guest'}</header>;
}
useMachine vs useActorRef: useMachine creates a new actor on mount and re-renders the component on every state change. useActorRef creates the actor but does NOT cause re-renders — combine with useSelector for surgical updates.
state.matches deep matching: For compound states, state.matches('playing.fastForward') checks nested states. For parallel states, pass an object: state.matches({ bold: 'on', italic: 'off' }).
useSelector for performance: Instead of re-rendering on every state change, select only what you need:
const isLoading = AppMachineContext.useSelector((state) => state.matches('loading'));
// Component only re-renders when isLoading changes
XState v5 with @xstate/react v4:
import { useMachine, useActorRef, useSelector } from '@xstate/react';
// useMachine still works the same
const [snapshot, send] = useMachine(machine);
// snapshot.value, snapshot.context, snapshot.matches() work identically
Testing components with machines: Pass a pre-configured machine or use @xstate/react/lib/test utilities. Override services to return mock data.
Common mistakes:
state.value === 'loading' instead of state.matches('loading') — breaks for compound statesuseMachine — the machine runs but invocations fail silentlyhttps://stately.ai/docs/xstate-react