背景
Angular の状態管理ライブラリ NGXS について、Action 定義を省略する手法として ngxs-labs/emitter というものがあるようなので、こちらを導入してみる。
ngxs-labs/emitter のメリットについて
ドキュメントを読むと、
- state で直接、 actions を登録するデコレーターを使える
- プロジェクト内で actions を作る必要がなくなる (本当に必要になるまでは)
ということなので、 actions を丸っと削除できるようだ。
イメージは下記画像のようになる。
Action がなくなるので、かなりスッキリしそうな予感。
インストール
bash
npm install @ngxs-labs/emitter
# or if you use yarn
yarn add @ngxs-labs/emitter
AppModule にインポート
src/app/app.module.ts
...
import { NgxsModule } from '@ngxs/store';
import { NgxsEmitPluginModule } from '@ngxs-labs/emitter';
...
@NgModule({
...
imports: [
...
NgxsModule.forRoot(...),
NgxsEmitPluginModule.forRoot()
],
...
})
export class AppModule { }
レシーバー
レシーバーは基本的な構成要素。
@Receiver()
は state 内の静的メソッドをデコレートできる関数で、そのメソッドをエミッターに渡す。
src/app/auth/auth.state.ts
import { Injectable } from '@angular/core';
import { Receiver } from '@ngxs-labs/emitter';
import { Selector, State, StateContext } from '@ngxs/store';
export interface AuthStateModel {
isAuthenticated: boolean;
}
@State<AuthStateModel>({
name: 'auth',
defaults: {
isAuthenticated: false
}
})
@Injectable()
export class AuthState {
@Receiver()
static setAuthenticated(ctx: StateContext<AuthStateModel>) {
ctx.setState({
isAuthenticated: true
});
}
@Receiver()
static setUnauthenticated(ctx: StateContext<AuthStateModel>) {
ctx.setState({
isAuthenticated: false
});
}
...
}
エミッター
エミッターは、基本的にコンポーネントとレシーバーの間の橋渡しをするもの。
@Emitter()
は、新しいゲッターを定義するプロパティを修飾する関数で、
、エミッタブルなインターフェースにアクセスできるようにする。
src/app/auth/auth.state.ts
import { Injectable } from '@angular/core';
import { Emittable, Emitter, EmitterAction, Receiver } from '@ngxs-labs/emitter';
import { Selector, State, StateContext } from '@ngxs/store';
// import { AuthAction } from './auth.actions';
export interface AuthStateModel {
isAuthenticated: boolean;
}
@State<AuthStateModel>({
name: 'auth',
defaults: {
isAuthenticated: false
}
})
@Injectable()
export class AuthState {
@Emitter(AuthState.setAuthenticated) static actSetAuthenticated: Emittable<void>;
@Emitter(AuthState.setUnauthenticated) static actSetUnauthenticated: Emittable<void>;
@Receiver()
static setAuthenticated(
ctx: StateContext<AuthStateModel>,
action: EmitterAction<void>
) {
ctx.setState({
isAuthenticated: true
});
}
@Receiver()
static setUnauthenticated(
ctx: StateContext<AuthStateModel>,
action: EmitterAction<void>
) {
ctx.setState({
isAuthenticated: false
});
}
@Selector()
static getIsAuth(state: AuthStateModel) {
return state.isAuthenticated;
}
}
src/app/auth/auth.service.ts
...
import { AuthState } from './auth.state';
@Injectable()
export class AuthService {
...
initAuthListener() {
this.afAuth.authState.subscribe(user => {
if (user) {
AuthState.actSetAuthenticated.emit();
...
} else {
...
AuthState.actSetUnauthenticated.emit();
...
}
});
}
...
}
感想
- かなりシンプルに書ける
- 例えば、dispatch と書いていたところを emit で書くと、分かりやすく、かつ短く書ける
- 無駄な action ファイルが不要でディレクトリが綺麗になる
実務でも使ってたが、より知見が深まった気がする。