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 の基本を学ぶ)試した。