Research
Security News
Malicious npm Packages Inject SSH Backdoors via Typosquatted Libraries
Socket’s threat research team has detected six malicious npm packages typosquatting popular libraries to insert SSH backdoors.
@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 OR one of the following Machine Config options: guards
, actions
, activities
, services
, delays
, immediate
, context
, or state
.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.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.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;
}
};
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>
);
};
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 (/* ... */)
}
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.
Research
Security News
Socket’s threat research team has detected six malicious npm packages typosquatting popular libraries to insert SSH backdoors.
Security News
MITRE's 2024 CWE Top 25 highlights critical software vulnerabilities like XSS, SQL Injection, and CSRF, reflecting shifts due to a refined ranking methodology.
Security News
In this segment of the Risky Business podcast, Feross Aboukhadijeh and Patrick Gray discuss the challenges of tracking malware discovered in open source softare.