Package Exports
- @xstate/react
This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (@xstate/react) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.
Readme
@xstate/react
Quick Start
- Install
xstateand@xstate/react:
npm i xstate @xstate/react- Import the
useMachinehook:
import { useMachine } from '@xstate/react';
import { Machine } from 'xstate';
const toggleMachine = Machine({
id: 'toggle',
initial: 'inactive',
states: {
inactive: {
on: { TOGGLE: 'active' }
},
active: {
on: { TOGGLE: 'inactive' }
}
}
});
export const Toggler = () => {
const [current, send] = useMachine(toggleMachine);
return (
<button onClick={() => send('TOGGLE')}>
{current.value === 'inactive'
? 'Click to activate'
: 'Active! Click to deactivate'}
</button>
);
};API
useMachine(machine, options?)
A React hook that interprets the given machine and starts a service that runs for the lifetime of the component.
Arguments
machine- An XState machine.options(optional) - Interpreter options OR one of the following Machine Config options:guards,actions,activities,services,delays,immediate,context, orstate.
Returns a tuple of [current, send, service]:
current- Represents the current state of the machine as an XStateStateobject.send- A function that sends events to the running service.service- The created service.
useService(service)
A React hook that subscribes to state changes from an existing service.
Arguments
service- An XState service.
Returns a tuple of [current, send]:
current- Represents the current state of the service as an XStateStateobject.send- A function that sends events to the running service.
useActor(actor)
A React hook that subscribes to messages (events) from actors, and can send messages (events) to actors.
Arguments
actor- An actor-like object, which has.subscribe(listener)and.send(event)methods.
Returns a tuple of [current, send]:
current- Represents the current message sent from the actor.send- A function that sends events to the actor.
Configuring Machines
Existing machines can be configured by passing the machine options as the 2nd argument of useMachine(machine, options).
Example: the 'fetchData' service and 'notifySuccess' action are both configurable:
const fetchMachine = Machine({
id: 'fetch',
initial: 'idle',
context: {
data: undefined,
error: undefined
},
states: {
idle: {
on: { FETCH: 'loading' }
},
loading: {
invoke: {
src: 'fetchData',
onDone: {
target: 'success',
actions: assign({
data: (_, event) => event.data
})
},
onError: {
target: 'failure',
actions: assign({
error: (_, event) => event.data
})
}
}
},
success: {
entry: 'notifySuccess',
type: 'final'
},
failure: {
on: {
RETRY: 'loading'
}
}
}
});
const Fetcher = ({ onResolve }) => {
const [current, send] = useMachine(fetchMachine, {
actions: {
notifySuccess: ctx => onResolve(ctx.data)
},
services: {
fetchData: (_, e) => fetch(`some/api/${e.query}`).then(res => res.json())
}
});
switch (current.value) {
case 'idle':
return (
<button onClick={() => send('FETCH', { query: 'something' })}>
Search for something
</button>
);
case 'loading':
return <div>Searching...</div>;
case 'success':
return <div>Success! Data: {current.context.data}</div>;
case 'failure':
return (
<>
<p>{current.context.error.message}</p>
<button onClick={() => send('RETRY')}>Retry</button>
</>
);
default:
return null;
}
};Matching States
Using a switch statement might suffice for a simple, non-hierarchical state machine, but for hierarchical and parallel machines, the state values will be objects, not strings. In this case, it's better to use state.matches(...):
// ...
if (current.matches('idle')) {
return /* ... */;
} else if (current.matches({ loading: 'user' })) {
return /* ... */;
} else if (current.matches({ loading: 'friends' })) {
return /* ... */;
} else {
return null;
}A ternary statement can also be considered, especially within rendered JSX:
const Loader = () => {
const [current, send] = useMachine(/* ... */);
return (
<div>
{current.matches('idle') ? (
<Loader.Idle />
) : current.matches({ loading: 'user' }) ? (
<Loader.LoadingUser />
) : current.matches({ loading: 'friends' }) ? (
<Loader.LoadingFriends />
) : null}
</div>
);
};Persisted and Rehydrated State
You can persist and rehydrate state with useMachine(...) via options.state:
// ...
// Get the persisted state config object from somewhere, e.g. localStorage
const persistedState = JSON.parse(localStorage.get('some-persisted-state-key'));
const App = () => {
const [current, send] = useMachine(someMachine, {
state: persistedState // provide persisted state config object here
});
// current will initially be that persisted state, not the machine's initialState
return (/* ... */)
}