1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

AngularのNGRXで状態管理にふれる

Posted at

はじめに

本記事はNGRXの紹介と、簡単なチュートリアルを通してNGRXをざっくりと理解することを目的にしています。また、Effectは別ライブラリとなり本記事には登場しません。そちらはまた別記事にてご紹介したいと思います。

NGRXの特徴

NGRXはRxJS(Reactive Extensions for JavaScript)を利用したAngularアプリケーションのためのグローバルな状態管理ライブラリで、Reduxに影響を受けて作られています。ReduxはFluxという設計思想のもと作られたライブラリで、単一方向のデータフローを特徴としており、主にReactでの利用を想定されています。

state-management-lifecycle.png

引用元:NGRX
https://ngrx.io/guide/store

上記の図はNgRxにおけるアプリケーション状態の全体的な一般フローを表しており、こちらの図を見ると単一方向のデータフローがわかるかと思います。

Storeというのはアプリケーションの状態(state)を保持する場所です。Storeのstateは直接変更するのではなく、必ずActionでDispatchを発行しReducerを経由して変更します。stateはSelectorを介して取得します。

Action -> Reducer -> Store -> Selector

※コンポーネントが破棄されるとストアデータも破棄されるNgRxComponentStoreもあります。

環境構築

  1. Node.jsのインストール
  2. Angular CLIのインストール
  3. プロジェクト作成
  4. 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を作成します。

src/app/counter.actions.ts
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は(その名の通り)初期値になります。

src/app/counter.reducer.ts
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に登録することにより、アプリケーション全体でカウンターアプリが利用できるようになります。

src/app/app.module.ts (StoreModule)
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
src/app/my-counter/my-counter.component.ts
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
  }
}
src/app/my-counter/my-counter.component.html
<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)に先ほど作成したコンポーネントを宣言します。

src/app/app.component.html
<app-my-counter></app-my-counter>

Moduleに登録する

MyCounterComponentをAppModuleの@NgModuledeclarationsプロパティに登録します。
declarationsプロパティに登録することでモジュール内でコンポーネント、ディレクティブやパイプを識別し、利用できるようになります。また、この中のいくつかをexportsプロパティを通して公開し、外部コンポーネントから使用できるようにすることもできます。

src/app/app.module.ts
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をディスパッチすることで、インクリメント、デクリメント、リセットの各メソッドを実装する。

src/app/my-counter/my-counter.component.ts
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

Animation.gif

おわりに

以上、NGRXのご紹介と簡単なチュートリアルを終わりたいと思います。次回はEffectに関する記事をご紹介します。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?