Angularの状態管理ライブラリ 「NGXS」に入門する
こんにちは!今年1月より入社しました、開発グループの太田原です。
私は現在、保育園向け業務支援サービス「KIDSNA キズナコネクト」の開発チームに所属しています。
KIDSNA キズナコネクトは園長先生や保育士さんの業務負荷を軽減するシステムであり、労務管理、保育料金管理などのバックオフィス系の機能に強みを持っています。
今回は、KIDSNA キズナコネクトにて使用しているAngularの状態管理ライブラリ「NGXS」について紹介したいと思います。
素敵なライブラリなのですが、まだまだ日本語情報がなく、私のような初心者(これまで業務でJavaScriptフレームワークを使ったこともなく、ましてや状態管理?Redux?Observable?なにそれ???な状態だった)にはとっつきづらいと感じたので、日本語の記事を書こうと思いました!
この記事では、Angular公式チュートリアルの「ツアー・オブ・ヒーローズ」をNGXSで状態管理したコードを見ながら、NGXS公式ドキュメントを解読していきます。また、NgRxとの違いについても調べてみます。
NGXSとは?
公式ドキュメント:https://ngxs.gitbook.io/ngxs
Angularの状態管理ライブラリとしては現在「NgRx」(NGXSと名前がすごく似ていますね!)がデファクトスタンダードだと思われます。
そんな中、NGXSは、NgRxやReduxに採用されている「CQRSパターン」を原型としつつ、ボイラープレートコードを削減しており、より簡潔に書くことができるのが強みらしいです!
(参考:https://ngxs.gitbook.io/ngxs/getting-started/why)
とは言っても、初心者にはよくわからない・・・なので、
作ってみよう
早速ですが、Angular公式チュートリアルの「ツアー・オブ・ヒーローズ」をNGXSで状態管理するように書き換えてみます。
NGXSのインストール
こちらにあるようにnpmまたはyarnで「@ngxs/store」パッケージをインストールします。
プラグインもいろいろありますが、「NGXS Logger-plugin」を入れておくとActionごとの状態変化をコンソールで見ることができるので便利です!
ツアー・オブ・ヒーローズにNGXSを適用
以下がツアー・オブ・ヒーローズにNGXSを適用したコードです。
また、以下がデモ環境(StackBlitz)です!
https://stackblitz.com/github/knts0/ngxs-tour-of-heroes
NGXS 4つの概念
サンプルアプリを見ながら、公式ドキュメントで紹介されているNGXSの4つの概念について理解していきます。
説明の都合上、公式ドキュメントと順番を変え、Action→State→Select→Storeとします。
これらは以下の図のような関係があります。StateはStoreが持っているイメージです。
Action
Actionは、アプリケーションの状態に変化を与えるものです。
今回、hero.actions.tsには以下のようなActionsを定義しています。各クラスのtypeフィールドがActionの識別子になっています。また、constructorに宣言しているフィールドは、各Actionに関連するメタデータを持たせるためのものです。
State
Stateは、NGXSで管理したいアプリケーションの状態の定義と、Actionのマッピングを行うクラスです。
(NgRxの「Reducers」「Effects」「Selectors」を組み合わせたようなものです。)
今回は、以下の値の状態を管理したいです。
- selectedHero(現在選択中のヒーロー)
- heroes(全ヒーローのリスト)
これらをHeroStateModelクラスのフィールドとします。そして、@Stateデコレータをつけた箇所でStateのメタデータを定義します。
また、Stateクラスでは、各Actionに対応する処理を定義します。
例えば、HeroAction.Addについては@Action(HeroAction.Add)デコレータのついたaddHero
メソッドでハンドリングされます。
このメソッドではStateContext<HeroStateModel>
型の「ctx」を引数に取っていますが、これは状態へのポインタと状態を変化させる機能を持っています。(以下では使用していませんが、StateContext#getState
メソッドで最新の状態を取得できます。)
addHero
メソッドでは、サーバーに新しいヒーローを追加する処理が実行されます。(Actionのメタデータはaction.payload
として取り出しています。)
追加後はHeroAction.Loadがdispatchされ、@Action(HeroAction.Load)デコレータのついたload
メソッドでハンドリングされます。サーバーからヒーロー一覧を取得し、状態の更新が行われます。
StateContext#patchState
は状態更新を簡潔に書けるメソッドです。(ここでは、HeroStateModelで更新のあるheroesプロパティのみ渡すことで、selectedHeroは値を変化させないようにしています。)
以上のように、NGXSではStateContextを用いることでStateクラス内で状態更新ロジックを書くことができます。NgRxのように、状態更新ロジックをReducerに切り出して記述しなくてもよくなります。
Select
SelectはStateの一部を読み出す機能です。これを使用するには以下の2つの方法があります。
Store
サービスのselect
メソッドを呼び出す@Select
デコレータを使用する
1つ目についてはStoreで説明するため、ここでは2つ目について見ていきます。例えば、hero.state.tsでは以下のように、HeroStateModelのうちの「heroes」プロパティのみを取り出すためのSelectorを宣言しています。
上記SelectorはDashBoardComponentで以下のように呼び出しています。これにより、Component内でObservableとして値を読み出すことができます。
Store
Storeは、Stateで定義した状態を保持し、各Componentと「Actions」を繋ぎ合わせたり、各ComponentにSelectorを提供します。
Componentで使用する際は以下のようにDIします。
以下、Storeの主な役割を説明していきます。
- 状態を保持する
アプリケーション内全ての、Stateクラスで定義された状態を保持しています。(今回については、HeroStateModel
クラスで定義したもののみ)
- Actionをdispatchする
Actionのdispatchとは、実行するActionを指示することです。
例えば、HeroesComponentでは以下のようにHeroAction.Add
をdispatchします。(引数「hero」はActionのところで説明したconstructor
のフィールドに代入されます。)
dispatchされると、Actionの説明のところで見たとおり、Stateクラスにおいて対応するメソッドが実行されます。
- SelectorをComponentに提供する
今回のアプリケーションでは使用していませんが、Store#select
メソッドを呼び出すことで、動的にStateの一部を読み出すことができます。@Select
デコレータによる静的なSelectorの宣言が必要ない場面などで有用です。
(参考:https://ngxs.gitbook.io/ngxs/concepts/select#store-select-function)
まとめ
まだまだ理解が浅いため、記述が誤っているところや、アドバイス等あればよろしくお願いいたします。
今回はツアー・オブ・ヒーローズの実装のために必要な機能の紹介にとどまりましたが、公式ドキュメントを見てみると他にもできることがたくさんあるのがわかります。これからどんどん詳しくなっていきたいです。
また、今後、日本語の情報が増えていくといいですね…!
告知
隔週木曜日に恵比寿のオフィスで夜活(交流会)を実施しています。
転職希望ではなくても、あるいはエンジニアでなくても、当社にご興味をお持ちの方は是非遊びに来てください!
綺麗なオフィスで軽食とお酒を楽しみましょう!