はじめに
本記事はNGRXの紹介と、簡単なチュートリアルを通してNGRXをざっくりと理解することを目的にしています。また、Effectは別ライブラリとなり本記事には登場しません。そちらはまた別記事にてご紹介したいと思います。
NGRXの特徴
NGRXはRxJS(Reactive Extensions for JavaScript)を利用したAngularアプリケーションのためのグローバルな状態管理ライブラリで、Reduxに影響を受けて作られています。ReduxはFluxという設計思想のもと作られたライブラリで、単一方向のデータフローを特徴としており、主にReactでの利用を想定されています。
引用元:NGRX
https://ngrx.io/guide/store
上記の図はNgRxにおけるアプリケーション状態の全体的な一般フローを表しており、こちらの図を見ると単一方向のデータフローがわかるかと思います。
Storeというのはアプリケーションの状態(state)を保持する場所です。Storeのstateは直接変更するのではなく、必ずActionでDispatchを発行しReducerを経由して変更します。stateはSelectorを介して取得します。
Action -> Reducer -> Store -> Selector
※コンポーネントが破棄されるとストアデータも破棄されるNgRxComponentStoreもあります。
環境構築
- Node.jsのインストール
- Angular CLIのインストール
- プロジェクト作成
- NGRXインストール
1. Node.jsのインストール
Node.jsは下記の記事などを参考にインストールしてください。
Node.jsをインストールする
また、この後のチュートリアルでは開発環境としてVisual Studio Codeを使用しています。こちらは必須ではありませんが、必要に応じてインストールしてください。
2. Angular CLIのインストール
Angular CLIはnpmからインストールします。
下記コマンドをターミナル(コマンドライン)で実行してください。
npm install -g @angular/cli
npm は基本的にNode.jsインストール時に合わせてインストールされます。上記コマンドが実行できない場合はnpmがインストールできてない可能性があります。
3. プロジェクト作成
CLIコマンド ng new
をターミナル(コマンドライン)で実行し、my-app
という名前を付けます。
ng new my-app
routing、stylesheetについて聞かれますが、今回はroutingは「No」、stylesheetは「css」で進めてください。
? Would you like to add Angular routing? No
? Which stylesheet format would you like to use? CSS
Visual Studio Codeでngコマンドを実行する際、エラーが出る場合があります。その場合は下記を実行し、PowerShellの実行ポリシーを変更してください。
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope Process
ng : このシステムではスクリプトの実行が無効になっているため、ファイル C:\Users\xxxxx\AppData\Roaming\npm\ng.ps1 を読み込むこと
ができません。詳細については、「about_Execution_Policies」(https://go.microsoft.com/fwlink/?LinkID=135170) を参照してください。
4. NGRXインストール
ターミナルで以下コマンドを打ち込み、NGRXをインストールします。
なお、ngrx/storeにはEffectは含まれていません。本チュートリアルではEffectは利用しませんが、必要に応じてEffectは別途インストールしてください。
npm install @ngrx/store --save
yarnやngコマンドを利用してインストールすることも可能です。
//yarn
yarn add @ngrx/store
//ng
ng add @ngrx/store@latest
チュートリアル
簡単なカウンターアプリを作成するチュートリアルを通してNGRXに触れたいと思います。
今回の記法はNGRX v7.4以降のcreateAction、createReducer、createEffectの関数を利用しています。これらを利用することにより、従来より簡潔に記載できるようになります。
Actionを作成する
カウンターアプリに必要なインクリメント、デクリメント、リセットのActionを作成します。
import { createAction } from '@ngrx/store';
export const increment = createAction('[Counter Component] Increment');
export const decrement = createAction('[Counter Component] Decrement');
export const reset = createAction('[Counter Component] Reset');
Reducerを作成する
作成した各Actionに紐づくReducerを作成します。ReducerにStoreの値を書き換える処理を記載します。なおinitialStateは(その名の通り)初期値になります。
import { createReducer, on } from '@ngrx/store';
import { increment, decrement, reset } from './counter.actions';
export const initialState = 0;
export const counterReducer = createReducer(
initialState,
on(increment, (state) => state + 1),
on(decrement, (state) => state - 1),
on(reset, (state) => 0)
);
Moduleに登録する
app.module.tsにStoreModule
と作成したcounterReducer
を登録します。
また、@NgModuleのimportsにStoreModule.forRoot
を登録します。StoreModule.forRoot
に登録することにより、アプリケーション全体でカウンターアプリが利用できるようになります。
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
import { StoreModule } from '@ngrx/store';
import { counterReducer } from './counter.reducer';
@NgModule({
declarations: [AppComponent],
imports: [BrowserModule, StoreModule.forRoot({ count: counterReducer })],
providers: [],
bootstrap: [AppComponent],
})
export class AppModule {}
コンポーネントを作成する
Actionを呼び出すコンポーネントを作成します。
ここではng g component
コマンドを利用してコンポーネントを作成しています。ngコマンドを利用するとコンポーネントに必要なファイルの生成に加えて、app.module.tsへの登録も行ってくれます。
(//TODOはこの後の工程で修正します)
ng g component my-counter
import { Component } from '@angular/core';
import { Observable } from 'rxjs';
@Component({
selector: 'app-my-counter',
templateUrl: './my-counter.component.html',
})
export class MyCounterComponent {
count$: Observable<number>
constructor() {
// TODO: Connect `this.count$` stream to the current store `count` state
}
increment() {
// TODO: Dispatch an increment action
}
decrement() {
// TODO: Dispatch a decrement action
}
reset() {
// TODO: Dispatch a reset action
}
}
<button (click)="increment()">Increment</button>
<div>Current Count: {{ count$ | async }}</div>
<button (click)="decrement()">Decrement</button>
<button (click)="reset()">Reset Counter</button>
AppComponentにコンポーネントを宣言する
app.component.html(AppComponent)に先ほど作成したコンポーネントを宣言します。
<app-my-counter></app-my-counter>
Moduleに登録する
MyCounterComponent
をAppModuleの@NgModule
のdeclarations
プロパティに登録します。
declarations
プロパティに登録することでモジュール内でコンポーネント、ディレクティブやパイプを識別し、利用できるようになります。また、この中のいくつかをexports
プロパティを通して公開し、外部コンポーネントから使用できるようにすることもできます。
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
import { StoreModule } from '@ngrx/store';
import { counterReducer } from './counter.reducer';
import { MyCounterComponent } from './my-counter/my-counter.component';
@NgModule({
declarations: [AppComponent, MyCounterComponent],
imports: [BrowserModule, StoreModule.forRoot({ count: counterReducer })],
providers: [],
bootstrap: [AppComponent],
})
export class AppModule {}
イベントとAction、Selectorを紐づける
コンポーネントでTODOとしていた処理にActionを設定します。
StoreにActionをディスパッチすることで、インクリメント、デクリメント、リセットの各メソッドを実装する。
import { Component } from '@angular/core';
import { Store } from '@ngrx/store';
import { Observable } from 'rxjs';
import { increment, decrement, reset } from '../counter.actions';
@Component({
selector: 'app-my-counter',
templateUrl: './my-counter.component.html',
})
export class MyCounterComponent {
count$: Observable<number>;
constructor(private store: Store<{ count: number }>) {
this.count$ = store.select('count');
}
increment() {
this.store.dispatch(increment());
}
decrement() {
this.store.dispatch(decrement());
}
reset() {
this.store.dispatch(reset());
}
}
アプリケーションを起動する
下記コマンドを実行するとアプリケーションが起動されます。
ng serve --open
おわりに
以上、NGRXのご紹介と簡単なチュートリアルを終わりたいと思います。次回はEffectに関する記事をご紹介します。