中安拓也のブログ

プログラミングについて書くブログ

Angular + Reduxを学ぶ #4 - Reducerを分割する

前回記事はこちら

引続き、angular-redux/storeの使い方を学んでいきます。今回はReducerを分割する方法について。

バージョン情報

Angular + Reduxの環境を使います

  • Angular: 5.2.9

  • angular-redux/store: 7.1.1

  • Node: 8.1.4

  • typescript: 2.5.3

  • webpack: 3.11.0

環境構築

Angular + Redux環境の構築方法について

  1. angular-cliでAngularプロジェクトを作成

  2. angular-redux/store インストール

  3. flux-standard-action インストール

npm install -g @angular/cli

ng new [適当なAngularのプロジェクト名]
cd [適当なAngularのプロジェクト名]

npm install redux --save @angular-redux/store

npm install redux --save flux-standard-action

サンプルアプリケーションについて

仮想通貨取引所のビットフライヤーとコインチェックのビットコイン価格を、WebAPIから取得して画面に表示するアプリケーションを例に説明します。

アプリケーションの構造

👇は、Reducer分割前のサンプルアプリケーションの図となります。ReducerがrootReducerのひとつしかなく、今後規模を拡大していくにあたって問題になりそうなので、分割します。

f:id:l08084:20180323195359p:plain

Stateの構造

本サンプルアプリケーションのStateは、3層にネストされていて、👇画像の構造になっています。Stateの2層目が2つに分かれているので、Reducerについても、(rootReducerも含めて)3つに分割します。

f:id:l08084:20180322201336p:plain

Stateのソースコード

Stateのソースコードについても載せておきます。

src/state/root/store.ts:

import { BitflyerTickerModel } from '../bitflyer-ticker/bitflyer-ticker.model';
import { CoincheckTickerModel } from '../coincheck-ticker/coincheck-ticker.model';

export interface IAppState {
  bitflyerTicker: BitflyerTickerModel;
  coincheckTicker: CoincheckTickerModel;
}

export const INITIAL_STATE: IAppState = {
  bitflyerTicker: null,
  coincheckTicker: null,
};

Reducerを分割する

ReduxのcombineReducersを使って、rootReducerを3つに分割します。

  • coincheckTickerReducer

  • bitflyerTickerReducer

  • rootReducer

f:id:l08084:20180324164818p:plain

分割前のReducer

分割前のReducerは、rootReducer一つです。

rootReducer

src/app/state/root/reducer.ts:

import { IAppState } from './store';
import { Action, combineReducers } from 'redux';
import { BitflyerTickerActions, BitflyerTickerAction } from '../bitflyer-ticker/bitflyer-ticker.action';
import { CoincheckTickerActions, CoincheckTickerAction } from '../coincheck-ticker/coincheck-ticker.action';

export function rootReducer(
    lastState: IAppState,
    action: Action
): IAppState {
  switch (action.type) {
    case BitflyerTickerActions.BITFLYER_SET_TICKER:
      return {
        bitflyerTicker: (action as BitflyerTickerAction).payload,
        coincheckTicker: lastState.coincheckTicker
      };
    case CoincheckTickerActions.COINCHECK_SET_TICKER:
      return {
        bitflyerTicker: lastState.bitflyerTicker,
        coincheckTicker: (action as CoincheckTickerAction).payload
      };
    default:
      return lastState;
  }
}

分割後のReducer

rootReducerを3つに分割しました。

  • coincheckTickerReducer

  • bitflyerTickerReducer

  • rootReducer

coincheckTickerReducer

分割した結果、StateのcoincheckTicker部分のみを担当するReducerになりました。

src/state/coincheck-ticker/coincheck-ticker.reducer.ts:

import { Action } from 'redux';
import { CoincheckTickerActions, CoincheckTickerAction } from '../coincheck-ticker/coincheck-ticker.action';
import { CoincheckTickerModel } from './coincheck-ticker.model';

export function coincheckTickerReducer(
    lastState: CoincheckTickerModel = null,
    action: Action
): CoincheckTickerModel {
  switch (action.type) {
    case CoincheckTickerActions.COINCHECK_SET_TICKER:
      return (action as CoincheckTickerAction).payload;
    default:
      return lastState;
  }
}
bitflyerTickerReducer

StateのbitflyerTicker部分のみを担当するReducerになっています。

src/state/bitflyer-ticker/bitflyer-ticker.reducer.ts:

import { Action } from 'redux';
import { BitflyerTickerActions, BitflyerTickerAction } from '../bitflyer-ticker/bitflyer-ticker.action';
import { BitflyerTickerModel } from './bitflyer-ticker.model';

export function bitflyerTickerReducer(
  lastState: BitflyerTickerModel = null,
  action: Action
): BitflyerTickerModel {
  switch (action.type) {
    case BitflyerTickerActions.BITFLYER_SET_TICKER:
      return (action as BitflyerTickerAction).payload;
    default:
      return lastState;
  }
}
rootReducer

combineReducersbitflyerTickerReducercoincheckTickerReducerを結合しています。

src/app/state/root/reducer.ts:

import { IAppState } from './store';
import { combineReducers } from 'redux';
import { bitflyerTickerReducer } from '../bitflyer-ticker/bitflyer-ticker.reducer';
import { coincheckTickerReducer } from '../coincheck-ticker/coincheck-ticker.reducer';

export const rootReducer = combineReducers<IAppState>({
    bitflyerTicker: bitflyerTickerReducer,
    coincheckTicker: coincheckTickerReducer,
});