Security News
Weekly Downloads Now Available in npm Package Search Results
Socket's package search now displays weekly downloads for npm packages, helping developers quickly assess popularity and make more informed decisions.
@xstate/react
Advanced tools
@xstate/react is a library that provides React hooks for using XState, a state machine and statechart library. It allows you to manage complex state logic in a more structured and predictable way by using finite state machines and statecharts.
Creating and Using State Machines
This feature allows you to create and use state machines in your React components. The code sample demonstrates a simple toggle button that switches between 'inactive' and 'active' states.
import { useMachine } from '@xstate/react';
import { createMachine } from 'xstate';
const toggleMachine = createMachine({
id: 'toggle',
initial: 'inactive',
states: {
inactive: { on: { TOGGLE: 'active' } },
active: { on: { TOGGLE: 'inactive' } }
}
});
function ToggleButton() {
const [state, send] = useMachine(toggleMachine);
return (
<button onClick={() => send('TOGGLE')}>
{state.matches('inactive') ? 'Off' : 'On'}
</button>
);
}
Using Context for Extended State
This feature allows you to use context for extended state in your state machines. The code sample demonstrates a counter that increments and decrements a count value stored in the machine's context.
import { useMachine } from '@xstate/react';
import { createMachine } from 'xstate';
const counterMachine = createMachine({
id: 'counter',
initial: 'active',
context: { count: 0 },
states: {
active: {
on: {
INCREMENT: { actions: 'increment' },
DECREMENT: { actions: 'decrement' }
}
}
}
}, {
actions: {
increment: (context) => context.count++,
decrement: (context) => context.count--
}
});
function Counter() {
const [state, send] = useMachine(counterMachine);
return (
<div>
<p>{state.context.count}</p>
<button onClick={() => send('INCREMENT')}>Increment</button>
<button onClick={() => send('DECREMENT')}>Decrement</button>
</div>
);
}
Invoking Services
This feature allows you to invoke services (e.g., API calls) within your state machines. The code sample demonstrates a state machine that fetches data from an API and handles loading, success, and failure states.
import { useMachine } from '@xstate/react';
import { createMachine, assign } from 'xstate';
const fetchMachine = createMachine({
id: 'fetch',
initial: 'idle',
context: { data: null, error: null },
states: {
idle: { on: { FETCH: 'loading' } },
loading: {
invoke: {
src: 'fetchData',
onDone: { target: 'success', actions: assign({ data: (context, event) => event.data }) },
onError: { target: 'failure', actions: assign({ error: (context, event) => event.data }) }
}
},
success: { on: { FETCH: 'loading' } },
failure: { on: { FETCH: 'loading' } }
}
}, {
services: {
fetchData: () => fetch('/api/data').then(response => response.json())
}
});
function FetchData() {
const [state, send] = useMachine(fetchMachine);
return (
<div>
{state.matches('idle') && <button onClick={() => send('FETCH')}>Fetch Data</button>}
{state.matches('loading') && <p>Loading...</p>}
{state.matches('success') && <pre>{JSON.stringify(state.context.data, null, 2)}</pre>}
{state.matches('failure') && <p>Error: {state.context.error}</p>}
</div>
);
}
Redux is a popular state management library for JavaScript applications. It provides a centralized store for state and uses actions and reducers to manage state changes. Unlike @xstate/react, which uses state machines and statecharts, Redux relies on a more traditional approach with a single global state and pure functions to handle state transitions.
MobX is a state management library that makes state observable and automatically updates the UI when the state changes. It uses reactive programming principles and provides a more flexible and less boilerplate-heavy approach compared to @xstate/react. While @xstate/react focuses on finite state machines, MobX emphasizes reactivity and simplicity.
Recoil is a state management library for React that provides a more fine-grained approach to state management. It allows you to create atoms (pieces of state) and selectors (derived state) that can be used in your components. Recoil offers a more granular and flexible way to manage state compared to the structured approach of state machines in @xstate/react.
xstate
and @xstate/react
:npm i xstate @xstate/react
useMachine
hook: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>
);
};
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 that you can pass in.Returns a tuple of [current, send, service]
:
current
- Represents the current state of the machine as an XState State
object.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 XState State
object.send
- A function that sends events to the running service.Existing machines are configurable with .useConfig(...)
. The machine passed into useMachine
will remain static for the entire lifetime of the component (it is important that state machines are the least dynamic part of the code).
::: tip
The useMemo
hook is an important performance optimization when creating a machine with custom config inside of a React component. It ensures that the machine isn't recreated every time the component rerenders.
:::
Example: the 'fetchData'
service and 'notifyChanged'
action are both configurable:
const fetchMachine = Machine({
id: 'fetch',
initial: 'idle',
context: {
data: undefined
},
states: {
idle: {
on: { FETCH: 'loading' }
},
loading: {
invoke: {
src: 'fetchData',
onDone: {
target: 'success',
actions: assign({
data: (_, e) => e.data
})
}
}
},
success: {
onEntry: 'notifyResolve',
type: 'final'
}
}
});
const Fetcher = ({ onResolve }) => {
const customFetchMachine = useMemo(
() =>
fetchMachine.withContext({
actions: {
notifyResolve: ctx => {
onResolve(ctx.data);
}
},
services: {
fetchData: (ctx, e) =>
fetch(`some/api/${e.query}`).then(res => res.json())
}
}),
[] // Machine should never change
);
const [current, send] = useMachine(customFetchMachine);
switch (current.state) {
case 'idle':
return (
<button onClick={() => send({ type: 'FETCH', query: 'something' })}>
Search for something
</button>
);
case 'loading':
return <div>Searching...</div>;
case 'success':
return <div>Success! Data: {current.context.data}</div>;
default:
return null;
}
};
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: 'frends' }) ? (
<Loader.LoadingFriends />
) : null}
</div>
);
};
FAQs
XState tools for React
The npm package @xstate/react receives a total of 535,986 weekly downloads. As such, @xstate/react popularity was classified as popular.
We found that @xstate/react demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 3 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
Socket's package search now displays weekly downloads for npm packages, helping developers quickly assess popularity and make more informed decisions.
Security News
A Stanford study reveals 9.5% of engineers contribute almost nothing, costing tech $90B annually, with remote work fueling the rise of "ghost engineers."
Research
Security News
Socket’s threat research team has detected six malicious npm packages typosquatting popular libraries to insert SSH backdoors.