TL;DR

JavaScript / TypeScript でステートマシンを実現する状態管理ライブラリ。イベント駆動型プログラミング、ステートマシン、ステートチャート、アクターモデルを使って、複雑なロジックを予測しやすい形で扱う。

なぜ必要か

一言でいうと、ありえない状態とイベントの組み合わせを防ぐためである。状態と遷移をマシンとして定義しておけば、定義にない遷移は起こらない。

仕組み

createMachine でマシンを定義し、createActor でアクターとして起動する。アクターにイベントを送ると、マシンの定義に従って状態が変わる。

import { createMachine, assign, createActor } from "xstate"
 
const countMachine = createMachine({
  context: { count: 0 },
  on: {
    INC: {
      actions: assign({
        count: ({ context }) => context.count + 1,
      }),
    },
  },
})
 
const countActor = createActor(countMachine).start()
countActor.subscribe((state) => console.log(state.context.count))
countActor.send({ type: "INC" }) // 1 が出力される

React、Vue、Svelte などのフロントエンドフレームワークと組み合わせられるほか、バックエンドでも JavaScript が動く場所ならどこでも使える。Node.js や Deno でサーバレス関数やサーバを作ることもできる。VS Code の拡張機能を使うと、コードから状態遷移図を確認できる。

注意点

すべての状態を XState で管理すべきではない。ボタンを押すとモーダルが表示されるだけの遷移を XState で管理すると、かえって過剰な設計になる。状態遷移から独立していて、操作の過程で変化しない値も、状態として持つべきではない。たとえば API のレスポンスはステートマシンの遷移で変わる値ではないので、useState で管理する。

一休では、XState で管理する基準として次の 2 つを挙げている。

  • 1 つのコンポーネントで useState が 3 つ以上定義されている
  • 何かアクションを起こしたときの遷移先が 2 つ以上ある

自分の理解

コードから状態遷移図を確認できるのは凄い。Alloy を彷彿とさせる。

入門の本と公式ドキュメントを見ながら、実際に動かしてみた。本の第 2 章は Node.js の代わりに Deno で(Deno を用いて xState の基本を学ぶ)、第 3 章は Next.js の代わりに SvelteKit で(Svelte を用いて xState の基本を学ぶ)試した。

関連

出典