---
title: "NgRx 8でアプリケーション開発をスピードアップ"
description: "NgRx 8は、開発者エルゴノミクスの改善、ボイラープレートの削減、ランタイムチェック、そしてエラー処理の簡易化を特徴としています。"
authors:
  - name: "Sam Julien"
    url: "https://auth0.com/blog/authors/sam-julien/"
date: "Jul 3, 2019"
category: "Developers,Whats New,NgRx"
tags: ["angular", "rxjs", "ngrx"]
url: "https://auth0.com/blog/jp-faster-app-development-with-ngrx-8/"
---

# NgRx 8でアプリケーション開発をスピードアップ


**概要：** NgRx 8に`createAction`や`createReducer`、`createEffect`など多くの改善機能が新たに追加されました。こうしたヘルパー機能によってボイラープレートが大幅に削減され、開発者の生産性が高まります。

## NgRx 8の圧倒的勝利
[Angular 8がリリース](https://auth0.com/blog/angular-8-differential-loading/)されて間もなく、NgRxチームからもバージョン8が発表されました。ライブラリに数多くの素晴らしい改良を加えたメジャーリリースです。ぜひ[Tim Deschryver氏](https://twitter.com/tim_deschryver)による[公式リリース発表](https://medium.com/ngrx/announcing-ngrx-version-8-ngrx-data-create-functions-runtime-checks-and-mock-selectors-a44fac112627)を読んでいただきたいのですが、ここにも概要を書いておきます。

- [John Papa氏](https://twitter.com/John_Papa)と[Ward Bell氏](https://twitter.com/wardbell)による[`angular-ngrx-data`](https://github.com/johnpapa/angular-ngrx-data)が[`@ngrx/data`](https://github.com/ngrx/platform/tree/master/modules/data)として正式にNgRxの一部になった。
- [ランタイムチェック](https://ngrx.io/guide/store/configuration/runtime-checks)を使ってステートとアクションに異変がなくシリアライズ可能であることをチェックできるようになった（これらは`ngrx-store-freeze`パッケージと置き換えられる）。
- [モックセレクタ](https://ngrx.io/guide/store/testing#using-mock-selectors)のおかげで完全に分離されたユニットテストを行えるようになった。
- 新たな[最小`routerState`オプション](https://next.ngrx.io/guide/router-store/configuration)など、`@ngrx/router`にいくつか改良が加わった。
- John Papa氏の[Angularスニペット](https://marketplace.visualstudio.com/items?itemName=johnpapa.Angular2)のVS CodeエクステンションにNgRxのスニペットが追加された。
-  [Alex Okrushko氏](https://twitter.com/AlexOkrushko)と[Wes Grimes氏](https://twitter.com/wesgrimes)がNgRxチームに加入した（納得！）。
  
ではそろそろ本命の話題に入りましょう。この記事のテーマは次のとおりです：

- `createAction`、`createReducer`、`createEffect`のヘルパーメソッドのおかげでボイラープレートが*劇的に*削減され、開発時間を短縮

以下にこれがどういうことかを詳しく説明し、これらの機能がどのように役立つのか探っていきますが、その前に、皆さんがこの新機能をフルに活用できるよう、バージョン8にアップデートする方法を復習しておきたいと思います。

<include src="TweetQuote" quoteText="NgRx 8は、開発者エルゴノミクスの改善、ボイラープレートの削減、ランタイムチェック、そしてエラー処理の簡易化を特徴としています！"/>

## NgRx 8へのアップデート方法
NgRx 8にアップデートするには、プロジェクトがAngular 8にアップデートされているかを確認する必要があります。次のコマンドを実行すると、Angular CLIをグローバルにアップデートできます。

```bash
npm install -g @angular/cli
```

Angular 8にアップデートしなくてはならないプロジェクト内で、次のコマンドを実行してください。

```bash
ng update @angular/cli @angular/core
```

その後、次のコマンドを実行すると、NgRx 8にアップデートできます。

```bash
ng update @ngrx/store
```

実はこの記事の執筆中、TypeScriptのバージョンに少々不具合が生じてしまいました。必要なバージョン間でコンフリクトが生じたようです。そのため、バージョン`3.4.3`で手動でロックしなければなりませんでした。

`ngrx-data`を使用している場合は次のコマンドで`@ngrx/data`に簡単に移行できます。

```bash
ng add @ngrx/data --migrate
```

このプロセスについては、Wes Grimes氏が[Angular 8とNgRx 8のアップグレード](https://dev.to/angular/how-to-upgrade-your-angular-and-ngrx-apps-to-v8-4iip)方法という素晴らしい記事で詳しく説明されています。NgRx 8への移行に関する具体的な詳細については、公式の[移行ガイド](https://ngrx.io/guide/migration/v8)を確認してください。最後に、Angularには新バージョンへの移行を支援してくれる[公式アップデートツール](https://update.angular.io)がありますのでお忘れなく。

では、いよいよお楽しみの本題に入ります。

## createExcitement：NgRx 8の新しいヘルパー機能
NgRx（およびReduxのパターン全般）についてよく聞かれる不平の1つとして、ボイラープレートがあまりにも多いことが挙げられます。しかしライブラリとパターンを調べてみると、大規模で複雑なアプリケーションで状態を管理している場合（そもそもNgRxが設計された理由はここにあります！）、初期設定はまさに価値ある投資だということが分かってきます。

とは言ってもNgRxチームは開発者コミュニティの声に耳を傾け、"開発者エルゴノミクス"（これは単に「コードを書きやすくする」という意味ですが）の大きな改良点をいくつかバージョン8に盛り込みました。こうした改良のほとんどは、アクション、リデューサ、エフェクトの設定方法のリファクタリングによるものです。これからは簡単に理解できてコードも少なくて済むヘルパーメソッドを利用できます。では順を追って見ていきましょう。

### createAction
私の友人[Mike Ryan氏](https://twitter.com/MikeRyanDev)は「アクションはNgRxという塊のグルテンだ」と言いますが、実際にアクションはNgRxアプリケーションの中核です。アプリケーションが必要としているアクションを決定するには細心かつ慎重な注意が求められます。残念なことにアクションの設定には大量のコードが必要です。例えば、私が以前書いた[NgRx認証](https://auth0.com/blog/ngrx-authentication-tutorial/)チュートリアルで構築したアプリケーションの抜粋を見てください。

```typescript
// src/auth/actions/auth.actions.ts（抜粋）
export enum AuthActionTypes {
  Login = '[Login Page] Login',
  LoginComplete = '[Login Page] Login Complete',
  LoginFailure = '[Auth API] Login Failure'
}

export class Login implements Action {
  readonly type = AuthActionTypes.Login;
}

export class LoginComplete implements Action {
  readonly type = AuthActionTypes.LoginComplete;
}

export class LoginFailure implements Action {
  readonly type = AuthActionTypes.LoginFailure;
  
  constructor(public payload: any) {}
}

export type AuthActions =
  | Login
  | LoginComplete
  | LoginFailure;
  ```

この抜粋に含まれるのはアプリケーションの8つのアクションのうち3つだけです。しかもこれは非常に小さいアプリケーションなのです！Alex Okrushko氏が[新しいアクションクリエーター](https://blog.angularindepth.com/ngrx-action-creators-redesigned-d396960e46da)と題した素晴しい記事で指摘しているように、enumとstringのせいもありアクションが使われている場所が非常に分かりにくくなっています。おまけにunion型ときた日にはたまったものではありません！ですから変更が必要だった理由はお分かりいただけましたね。

そこで登場したのが`createAction`メソッドです。`createAction`では、アクション名を、オプションで特定の引数タイプを分割されたオブジェクトとする関数`props`と一緒に渡します。 

おっと、これでは難解な用語が多すぎますね。例を用いて説明しましょう。 

その作成時に`Book[]`型の本（`books`）の配列を受け取る必要のある、`booksLoaded`というアクションがあるとします。ここでは`createAction`用の`props`関数は`props<{ books: Book[] }>()`になります（[分割](https://auth0.com/blog/a-rundown-of-es6-features/#ECMAScript-2015-Features)により`{ books }`は`{ books: books }`と同等になります）。  

```typescript
export const booksLoaded = createAction(
  '[Books API] Books Loaded', 
  props<{ books: Book[] }>()
  );
  ```

この方法では、この型の新しいアクションを作るときにコンストラクタ`new booksLoaded({ booksFromServer })`に直接bookの配列を渡すことができます。

すると、上記に示した認証アクションは次のようにリファクタリングされます。

```typescript
export const login = createAction('[Login Page] Login');

export const loginComplete = createAction('[Login Page] Login Complete');

export const loginFailure = createAction(
  '[Auth API] Login Failure', 
  props<{ error: any }>()
  );
```

どうですか、すごいでしょう？

ここで気を付けたい点が2つあります。まず、アクションの定数名がPascalケース（`LoginFailure`）からCamelケース（`loginFailure`）に変わっていますね。これは、アクションがクラスから関数に変わったためで、JavaScriptでは標準の表記法です。これに惑わされず、リファクタリングの際には注意してください。

2つ目は、名前を分かりやすくするため`payload`から`error`に変更したことです（`Error`のような新しいタイプにすることも可能ですが、今回の例ではあまり問題になりません）。これは、アクションクリエータ関数が再設計された素晴らしい新機能ですが、使用時には注意してください。`payload`プロパティに依存するアプリケーションの他の部分を壊さないように、コードをよく検索してください（[Gregor Woisode氏](https://medium.com/@gregor.woiwode/ngrx-8-meet-the-new-upcoming-factory-methods-of-the-next-major-release-a97a079cc089)からもリマインダが出ています）。アクションクリエータで`payload`の名前を変更する場合、必ずすべての箇所で変更してください。

最後に、新アクションクリエータには素晴しい機能がもう1つあります。それは`prop`引数のデフォルト値を指定できるようになったことです。例えば、上記の`loginFailure`アクションで、次のデフォルトのエラーメッセージを提供できます。

```typescript
export const loginFailure = createAction(
  '[Auth API] Login Failure',
  (errorMessage = 'Error logging in') => ({ payload: { errorMessage }})
  );
```

これは、何らかの理由でサーバーがエラーメッセージを提供できない場合のバックアップとして有効です。

このような新アクションクリエータをアプリケーションに追加するために、[アップデートされたアクションschematic](https://ngrx.io/guide/schematics/action)を次のように使用できます。

```bash
ng generate @ngrx/schematics:action myNewAction --creators
```

このコマンドは、`createAction`関数を使用して、`loadMyNewAction`という名前の定数を含む`my-new-action.actions.ts`と呼ばれる新しいファイルを生成します。

<include src="TweetQuote" quoteText="NgRx 8の新アクションクリエータは、新機能を追加する際の開発時間を劇的に削減します！"/>

### createReducer
新しいアクション作成機能は素晴らしいですが、ほかにも改良点があります。リデューサもまた、`createReducer`メソッドのおかげで簡単に書けるようになりました。

ここではバーガーレストランのアプリケーションなどに見られるリデューサの典型例をお見せしましょう。

```typescript
export function reducer(
  state = initialState,
  action: BurgerApiActionsUnion
  ): State {
  switch (action.type) {
    case BurgerApiActionTypes.LoadBurgerCollection: {
      return { 
        ...state
        loading: true
      };
    }
    
    case BurgerApiActionTypes.LoadBurgersSuccess: {
      return { 
        loaded: true,
        loading: false,
        ids: action.payload.map(burger => burger.id)
      };
    }
    
    default: {
      return state;
    }
  }
}
```

これは簡単な例ですが、スケールアップするにつれてcase文の数が増大し、コードが分かりにくくなります。すぐに収拾がつかない状態になってしまいます。まるでハンバーガーを食べるときのように。

それがNgRx 8では、次のように代わりに`createReducer`を使えます。

```typescript
const burgerReducer = createReducer(
  initialState,
  on(BurgerApiActionTypes.loadBurgerCollection, state => ({
    ...state,
    loading: true
  })),
  on(BurgerApiActionTypes.loadBurgersSuccess, (state, { burgers }) => ({
    loaded: true,
    loading: false,
    ids: burgers.map(burger => burger.id)
  }))
  );
```

この方がずっと分かりやすいですね。各caseが、アクションと状態を変更する関数を受け取る`on`関数を使用します。 

`on`関数は複数のアクションを受け入れることができるので、リデューサ関数を簡単に拡張できます。例えば、レコードの追加に成功した場合とレコードの削除に失敗した場合、その結果得られるステートは同じです。その良い例が、データのコレクション（サーバー上のものと一致する必要あり）と、検索を高速化するためIDの配列（メモリに保存）の両方がアプリケーションステートに含まれている場合です。IDの配列がサーバー上のものと常に確実に一致するようにするには、レコードの追加や削除に失敗する可能性を考慮する必要があります。

バーガーアプリケーションの例で見てみましょう。

```typescript
const burgerReducer = createReducer(
  // ...その他のcase、
  on(
    CollectionApiActions.addBurgerSuccess,
    CollectionApiActions.removeBurgerFailure,
    (state, { burger }) => {
      if (state.ids.indexOf(burger.id) > -1) {
        return state;
      }
      return {
        ...state,
        ids: [...state.ids, burger.id],
      };
    }
  ),
  on(
    CollectionApiActions.removeBurgerSuccess,
    CollectionApiActions.addBurgerFailure,
    (state, { burger }) => ({
      ...state,
      ids: state.ids.filter(id => id !== burger.id),
    })
  )
  );
```

複数のアクションに同じリデューサケースを使用すると、アプリケーションで行われている処理をより簡単に把握できるだけでなく、`ids`配列が常にサーバーとの同期を保てます。

`createReducer`を使用する上で注意すべきことがもう1つあります。本稿の執筆時点で、AngularのAhead-of-time（AOT）コンパイラ（プロダクションビルドの作成に使用するコンパイラ）[は、関数式](https://angular.io/guide/aot-compiler#function-calls-are-not-supported)に対応していません。そのため、エクスポートされた関数内で`createReducer`関数を次のようにラップする必要があります。

```typescript
export function reducer(state: State | undefined, action: Action) {
  return burgerReducer(state, action);
}
```

最後に、`createAction`の場合と同様に、[新しいリデューサスタイルのschematic](https://ngrx.io/guide/schematics/reducer)を利用できます。

```bash
ng generate @ngrx/schematics:reducer myNewReducer --creators
```

このschematicで`my-new-reducer.reducer.ts`と呼ばれる新しいファイルが生成されます。これには`createReducer`定数とAOT用にエクスポートされた`reducer`関数の両方が含まれます。

### createEffect
最後に、エフェクトの作成を改善するために追加された`createEffect`についてです。これまでエフェクトは`@Effect()`デコレータを使用していました。これはこれで良かったのですが、NgRxで使われる他のより関数的なアプローチに比べると、ちょっと風変わりな存在でした。

バーガーアプリケーションの例に戻って、旧式のエフェクト例を見てみましょう。 

```ts
@Effect()
loadBurgerCollection$ = this.actions$.pipe(
  ofType(BurgerApiActionTypes.LoadBurgerCollection),
  switchMap(() =>
    this.burgerService.getAllBurgers().pipe(
      map((burgers: Burger[]) =>
        BurgerApiActions.LoadBurgersSuccess({ burgers })
      ),
      catchError(error =>
        of(BurgerApiActions.LoadBurgersFailure({ error }))
      )
    )
  )
  );
```

新しい`createEffect`ヘルパーではこれを少し簡略化して標準化し、アクション、リデューサ、セレクタにより近い感じになっています。

```typescript
loadBurgerCollection$ = createEffect(() =>
  this.actions$.pipe(
    ofType(BurgerApiActionTypes.loadBurgerCollection),
    switchMap(() =>
      this.burgerService.getCollection().pipe(
        map((burgers: Burger[]) =>
          BurgerApiActions.loadBooksSuccess({ burgers })
        ),
        catchError(error =>
          of(BurgerApiActions.loadBooksFailure({ error }))
        )
      )
    )
  )
  );
```

> アクションはクラスから関数に変更されたため、PascalケースからCamelケースに変わったことを忘れないでください。

また、新しいアクションをディスパッチしないエフェクトに小さな変更が加えられています。次はバーガーが正しく注文された後にトリガされるエフェクトですが、リダイレクトされてホームページに戻り、新しいアクションをディスパッチしません。

```typescript
@Effect({ dispatch: false })
orderBurgerSuccess$ = this.actions$.pipe(
  ofType(BurgerApiActions.OrderBurgerSuccess),
  tap(() => {
    this.router.navigate(['/']);
  })
  );
```

新しい`createEffect`関数を使用すると、このコードを次のようにリファクタリングできます。

```typescript
orderBurgerSuccess$ = createEffect(() => 
  this.actions$.pipe(
    ofType(BurgerApiActions.LoginSuccess),
    tap(() => this.router.navigate(['/']))
  ),
  { dispatch: false }
  );
```

`createEffect`関数の2番目の引数は、エフェクトの動作を制御するメタデータで渡すためのものです。

NgRx 8のエフェクトについて最後に一言。これまでは、開発者がエフェクト内のエラー処理を忘れているか、適切に処理していないかのどちらかだと考えられていました。この問題を緩和するために、これからはエフェクトが自動的にエラー時に（完了するのでなく）サブスクライブし直し、ディスパッチされるアクションを待機し続けます。これはとても便利ですが、もしも動作が変になり始めたら無効にすることもできます。その場合は`{ resubscribeOnError: false }`を`createEffect`関数の2番目の引数としてパスするだけです。 

他の2つの例と同様に、アプリケーションに[エフェクトの新しいスタイルを追加するschematic](https://ngrx.io/guide/schematics/effect)があるのでご覧ください。

```bash
ng generate @ngrx/schematics:effect myNewEffect --creators
```

このコマンドは、`createEffect`をインポートして`Injectable`デコレータでマークされたクラスを作成する、`my-new-effect.effects.ts`という新しいファイルを生成します。

## ついでに："NgRxは不要"なときでも認証が必要になる場合があります
今日のアプリケーションでは、ユーザープロファイル情報を保持して認証と認可を処理するために、何らかのアイデンティティ管理が必要となることがほとんどです。NgRxアプリケーションでアイデンティティ管理戦略を実装する場合、バージョン8で構文の一部が変更されたものの、その中核をなす原理に変わりはありません。ログインとログアウトにはアクションを、認証サービスの呼び出しにはエフェクトを使用する一方で、ログインのステータスやプロファイル属性などの状態の変更はリデューサによって処理します。

この詳細については、[NgRx認証チュートリアル](https://auth0.com/blog/ngrx-authentication-tutorial/)に書いておきました。ぜひ<a href="https://auth0.com/signup" data-amp-replace="CLIENT_ID" data-amp-addparams="anonId=CLIENT_ID(cid-scope-cookie-fallback-name)">にアクセスして無料のAuth0アカウントにご登録ください</a>。チュートリアルを使ってすぐに作業を始められるようになります。

![Auth0の軽量かつユニバーサルなログインモーダル画面](https://images.ctfassets.net/23aumh6u8s0i/54lZ5srDBAM0dqWSRkdxXk/f4260dcf2af6e7b5b4a52c62769e0745/lightweight-login)

ところで、バージョン8の新ヘルパー機能を使用してみた感想をぜひお聞きかせください。いつでも[コミュニティフォーラム](https://community.auth0.com/)にてご連絡ください。

## 終わりに
皆さんはどうか分かりませんが、私はNgRx 8にとても満足しています。このチームは、素晴らしいソフトウェアを作り、[phenomenal docs](https://ngrx.io/docs)の書き込みも行っているボランティアの集まりです。このバージョンには改良点がたくさん盛り込まれています。将来は明るいですね。皆さんも実際に試してみて、ぜひ感想をお聞かせください！

