SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

現場で役立つ! React向けライブラリ詳説

ステートマシンを実装するためのJavaScriptライブラリ「XState」の基本的なAPIを解説

現場で役立つ! React向けライブラリ詳説 第11回

  • X ポスト
  • このエントリーをはてなブックマークに追加

アクションとサービス

 次は、ステートマシンにおけるアクションとサービスについて解説します。アクションは、ステート遷移時に実行される副作用を持つ処理です。サービスは、非同期処理や長時間実行されるタスクを扱うための機能です。

アクション

 アクションは、状態遷移時に副作用として実行される処理です。アクションは、actionsプロパティを使用して定義されます(リスト8)。

[リスト8]アクションの例(src/vending.machine.js)
import { createMachine, assign } from "xstate";

export const machine = createMachine(
  {
    id: "vending-machine",
    description: "自動販売機",
    initial: "idle",
    context: {
      // 投入した金額
      amount: 0,
    },
    states: {
      idle: {
        description: "初期状態",
        on: {
          INSERT_COIN: {
            description: "お金を投入する",
            target: "inserting",
            // (1) コインの追加を処理するアクション
            actions: assign((context, event) => { // (2)
              // eventは { amount: 100 } のような投入金額についてのオブジェクト
              return {
                amount: context.amount + event.amount, // (3)
              };
            }),
          },
        },
      },
      // その他のステートを定義
    },
  }
);

 (1)で、INSERT_COIN イベントによる inserting への状態遷移が発生した場合に発動する処理を actions に記述しています。actions に関数を登録すると、現在の context の値と、UIから send メソッドなどで送られた event オブジェクトが引数として受け取れます。

 (2)で使用している assign 関数は context を更新する際に使用する高階関数です。 assign の引数に渡した関数で(3)のように更新したいプロパティ(今回の例では amount)と新しい値をオブジェクトに記載して戻り値として返すと、context 内で該当のプロパティが更新されます。

 リスト8では、INSERT_COIN イベントが発生したときに、inserting ステートに遷移するとともに、amount コンテキストの値を更新するアクションが実行されます。

サービス

 サービスは、非同期処理や長時間実行されるタスクを扱うための機能です。サービスは、各ステートの定義内の invoke プロパティを使用して定義されます(リスト9)。

[リスト9]サービスの例(src/vending.machine.js)
import { createMachine } from 'xstate';

// (3)
const fetchItemDetails = async (context, event) => {
  // API呼び出しや非同期処理を実行
};

const vendingMachine = createMachine({
  id: 'vendingMachine',
  initial: 'idle',
  states: {
    idle: {
      on: {
        SELECT_ITEM: 'fetchingItemDetails',
      },
    },
    fetchingItemDetails: { // (1)
      invoke: {
        src: fetchItemDetails, // (2) サービスとして実行される関数
        onDone: { // (4)
          target: 'itemDetailsFetched',
          actions: /* ... */,
        },
        onError: { // (5)
          target: 'error',
          actions: /* ... */,
        },
      },
    },
    // その他のステートを定義
  },
});

 この例では、(1)の fetchingItemDetails ステートに遷移したときに、 (2)で指定した fetchItemDetails 関数がサービスとして実行されます。(3)の fetchItemDetails 関数で実装した非同期処理が成功すると、(4)の onDone プロパティに定義されたステートへ遷移し、エラーが発生した場合には(5)の onError プロパティに定義されたステートへ遷移します。(2)にはPromiseを登録できるので、(3)のようにasync関数でサービスを定義すると、通信処理などは実装しやすいかもしれません。

アクションとサービスの違い

 アクションとサービスは少し似ていますが、同期か非同期か、処理結果によってステートが変わるかどうかなど、細かい点で違いがあります。違いを図で確認してみましょう。アクションを実装したリスト8を図示すると図1のようになります。

図2:アクションの図
図2:アクションの図

 idle ステートから INSERT_COIN イベントが発行されると、inserting ステートに遷移する前に、INSERT_COIN イベントに登録された actions が実行され、context.amountの数値が加算されます。重要なのは、処理結果によって遷移先のステートが変化することはない、ということです。加算した結果が200円でも500円でも、必ず inserting ステートに遷移します。また、actions で実行できるのは同期的な処理のみです。

 一方、サービスを実装したリスト9を図示すると、図2のようになります。

図3:サービスの図
図3:サービスの図

 idle ステートから SELECT_ITEM イベントが発行されると、fetchingItemDetails ステートに遷移します。fetchingItemDetails ステートには invoke プロパティが定義されているので、遷移してきた直後に fetchItemDetails() が実行されます。その結果を受けて、成功していれば、onDone に登録した itemDetailsFetched ステートに遷移しますし、逆に失敗していれば、onError に登録した error ステートに遷移します。ここで重要なのは、処理結果によって遷移先のステートが決定される、ということです。処理内容は同期処理でも非同期処理(Promise)でも構いません。

 アクションとサービスは、似ているようで違いがあります。

  • イベントに副作用を持たせて context などを更新したり console.log でイベントのパラメータを確認したい場合などにはアクションを使う
  • 通信の結果などに応じて次のステートを決めたい場合などにはサービスを使う

 というように、使い分けるとよいでしょう。

まとめ

 XStateで定義できるステートマシンの、基本的な構成要素を解説しました。本記事を読んだ後で、前回の記事をあらためて読んでいただけると、実用する際のイメージがつけやすくなるはずです。

この記事は参考になりましたか?

  • X ポスト
  • このエントリーをはてなブックマークに追加
現場で役立つ! React向けライブラリ詳説連載記事一覧

もっと読む

この記事の著者

WINGSプロジェクト 中川幸哉(ナカガワユキヤ)

WINGSプロジェクトについて>有限会社 WINGSプロジェクトが運営する、テクニカル執筆コミュニティ(代表 山田祥寛)。主にWeb開発分野の書籍/記事執筆、翻訳、講演等を幅広く手がける。2018年11月時点での登録メンバは55名で、現在も執筆メンバを募集中。興味のある方は、どしどし応募頂きたい。著書記事多数。 RSS X: @WingsPro_info(公式)、@WingsPro_info/wings(メンバーリスト) Facebook

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

山田 祥寛(ヤマダ ヨシヒロ)

静岡県榛原町生まれ。一橋大学経済学部卒業後、NECにてシステム企画業務に携わるが、2003年4月に念願かなってフリーライターに転身。Microsoft MVP for Visual Studio and Development Technologies。執筆コミュニティ「WINGSプロジェクト」代表。主な著書に「独習シリーズ(Java・C#・Python・PHP・Ruby・JSP&サーブレットなど)」「速習シリーズ(ASP.NET Core・Vue.js・React・TypeScript・ECMAScript、Laravelなど)」「改訂3版JavaScript本格入門」「これからはじめるReact実践入門」「はじめてのAndroidアプリ開発 Kotlin編 」他、著書多数

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

  • X ポスト
  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/17739 2023/05/25 11:00

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング