Skip to content

Latest commit

History

History
140 lines (114 loc) 路 3.38 KB

xstate.mdx

File metadata and controls

140 lines (114 loc) 路 3.38 KB
title description nav
XState
This doc describes XState integration.
4.04

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

You have to install xstate and jotai-xstate to use this feature.

npm install xstate jotai-xstate
# or
yarn add xstate jotai-xstate

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)

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

Check examples with atomWithMachine:

Restartable machine:

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.)