XState
Jotai’s state management is primitive and flexible, but that sometimes means too free. XState is a sophisticated library to provide a better and safer abstraction for state management.
Install
Section titled “Install”You have to install xstate and jotai-xstate to use this feature.
npm install xstate jotai-xstateatomWithMachine
Section titled “atomWithMachine”atomWithMachine creates a new atom with XState machine.
It receives a function getMachine to create a new machine.
getMachine is invoked at the first use with get argument,
with which you can read other atom values.
import { useAtom } from 'jotai'import { atomWithMachine } from 'jotai-xstate'import { assign, createMachine } from 'xstate'
const createEditableMachine = (value: string) => createMachine<{ value: string }>({ id: 'editable', initial: 'reading', context: { value, }, states: { reading: { on: { dblclick: 'editing', }, }, editing: { on: { cancel: 'reading', commit: { target: 'reading', actions: assign({ value: (_, { value }) => value, }), }, }, }, }, })
const defaultTextAtom = atom('edit me')const editableMachineAtom = atomWithMachine((get) => // `get` is available only for initializing a machine createEditableMachine(get(defaultTextAtom)),)
const Toggle = () => { const [state, send] = useAtom(editableMachineAtom)
return ( <div> {state.matches('reading') && ( <strong onDoubleClick={send}>{state.context.value}</strong> )} {state.matches('editing') && ( <input autoFocus defaultValue={state.context.value} onBlur={(e) => send({ type: 'commit', value: e.target.value })} onKeyDown={(e) => { if (e.key === 'Enter') { send({ type: 'commit', value: e.target.value }) } if (e.key === 'Escape') { send('cancel') } }} /> )} <br /> <br /> <div> Double-click to edit. Blur the input or press <code>enter</code> to commit. Press <code>esc</code> to cancel. </div> </div> )}Restartable machine stored in a global Provider (provider-less mode)
Section titled “Restartable machine stored in a global Provider (provider-less mode)”When your machine reaches its final state it cannot receive any more events.
If your atomWithMachine is initialized in global store (aka provider-less mode),
to restart it you need to send a RESTART event to your machine like so:
import { RESTART } from 'jotai-xstate'
const YourComponent = () => { const [current, send] = useAtom(yourMachineAtom)
const isFinalState = current.matches('myFinalState')
useEffect(() => { // restart globally initialized machine on component unmount return () => { if (isFinalState) send(RESTART) } }, [isFinalState])}Examples
Section titled “Examples”Check examples with atomWithMachine:
Restartable machine:
Tutorials
Section titled “Tutorials”Check out a course about Jotai and XState.
Complex State Management in React with Jotai and XState
(Note: In the course, it uses jotai/xstate which is supersede by jotai-xstate.)