【Angular】Ngrx概要
-
Reduxの思想をAngularで実装するためのライブラリ
-
Effectsなど拡張機能あり
-
用語集
Component
-
Angularのコンポーネント
Action
-
「何が起こったか」という情報を持ったオブジェクト
-
コンポーネントからのイベントをトリガーとしてActionオブジェクトをStoreにDispatchする
Reducer
-
ActionオブジェクトとStateを受け取りStateを更新する
-
更新する場合はStateを新規のオブジェクトとして作成する
Store
-
Stateを保存する場所
Selector
-
Storeから特定のデータを取り出す
Effects
Service
-
Angularのサービス
実装
インストール
Store
npm install @ngrx/store --save
ng add @ngrx/store@latest
Effects
-
使う人だけ
npm install @ngrx/effects --save
ng add @ngrx/effects@latest
Devtool
-
使う人だけ
-
productionビルド時はログ参照のみになる
npm install @ngrx/store-devtools --save
ng add @ngrx/store-devtools@latest
<参考>
https://ngrx.io/guide/store/install
https://ngrx.io/guide/store-devtools/install
https://ngrx.io/guide/effects/install
Storeの作成
-
Stateとして管理するものを定義
store-definition.ts
import { LoggedInReducer, UserIdReducer } from '../reducer/user-reducer';
/**
* Key定義
*/
export enum Storekey {
isloggedIn = 'isloggedIn',
userId = 'userId',
}
/**
* Store定義
*/
export const StoreDefinition = {
isloggedIn: LoggedInReducer,
userId: UserIdReducer,
};
/**
* 型定義
*/
export interface AppStore {
isloggedIn: boolean;
userId: string;
}
Actionの作成
-
Stateを参照するActionを定義
user-actions.ts
import { createAction, props } from '@ngrx/store';
/**
* ユーザーAction
*/
export const UserActions = {
login: createAction('user/login'),
logout: createAction('user/logout'),
// Actionの引数としてuserIdを定義
addUser: createAction('user/add', props<{ userId: string }>()),
deleteUser: createAction('user/delete'),
};
Reducerの作成
-
Actionに紐づく処理を定義
user-reducers.ts
import { Action, createReducer, on } from '@ngrx/store';
import { UserActions } from '../action/user-actions';
/** ログイン状態 */
export const loggedIn: boolean = false;
/** ユーザーID */
export const userId: string = '';
const loggedInReducer = createReducer(
loggedIn,
on(UserActions.login, (state) => {
// 変更後のstateを返却(新しいオブジェクトとして作成)
return true;
}),
on(UserActions.logout, (state) => {
return false;
})
);
const userIdReducer = createReducer(
userId,
// Actionの引数としてuserIdを受け取る
on(UserActions.addUser, (state, { userId }) => {
return userId;
}),
on(UserActions.deleteUser, (state) => {
return '';
})
);
export function LoggedInReducer(state: boolean, action: Action) {
return loggedInReducer(state, action);
}
export function UserIdReducer(state: string, action: Action) {
return userIdReducer(state, action);
}
NgModuleに設定
-
インストール時のng addで自動で追加される
-
Store定義を手動で追加
app.module.ts
import { StoreModule } from '@ngrx/store';
import { StoreDefinition } from './state/store/store-definition';
import { StoreDevtoolsModule } from '@ngrx/store-devtools';
imports: [
// Store定義を追加
StoreModule.forRoot(StoreDefinition),
// Devtoolの設定
StoreDevtoolsModule.instrument({
maxAge: 25,
logOnly: environment.production,
}),
]
Componentから呼び出す
-
定義したActionをStoreにDispatchする
-
入力した内容でStateを更新する
-
遷移先のComponentで更新されたStateを取得する
遷移元component
import { Router } from '@angular/router';
import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { Store } from '@ngrx/store';
import { Observable } from 'rxjs';
import { UserActions } from 'src/app/state/action/user-actions';
import { AppStore, Storekey } from 'src/app/state/store/store-definition';
@Component({
selector: 'app-test',
templateUrl: './test.component.html',
styleUrls: ['./test.component.scss'],
})
export class TestComponent implements OnInit {
constructor(
private fb: FormBuilder,
private router: Router,
private store: Store<AppStore>
) {}
public sampleForm: FormGroup;
ngOnInit() {
this.initForm();
}
initForm() {
this.sampleForm = this.fb.group({
userId: ['', Validators.required],
password: ['', Validators.required],
});
}
login(value: any) {
// ログインActionをdispatch
this.store.dispatch(UserActions.login());
// ユーザーID追加Actionをdispatch
this.store.dispatch(UserActions.addUser({ userId: value.userId }));
// 画面遷移
this.router.navigate(['/state']);
}
}
遷移先component
import { Component, OnInit } from '@angular/core';
import { Store } from '@ngrx/store';
import { Observable } from 'rxjs';
import { AppStore, Storekey } from 'src/app/state/store/store-definition';
@Component({
selector: 'app-state',
templateUrl: './state.component.html',
styleUrls: ['./state.component.scss'],
})
export class StateComponent implements OnInit {
/** ログイン状態 */
isloggedIn$: Observable<boolean>;
/** ユーザーID */
userId$: Observable<string>;
constructor(private store: Store<AppStore>) {
// storeから取得
this.isloggedIn$ = store.select(Storekey.isloggedIn);
this.userId$ = store.select(Storekey.userId);
}
ngOnInit(): void {}
}
遷移先html
<!-- 取得したstateを表示 -->
<p>ログイン状態:{{isloggedIn$ | async}}</p>
<p>ユーザーID:{{userId$ | async}}</p>