前章とは違い、本章はとても簡単で、ちょっとした変更を行うだけです。
まず、コードベースにImmutable JSを追加します。Immutableはオブジェクトを変更(mutating)することなしに扱うためのライブラリです。例えば以下のようにする代わりに:
const obj = { a: 1 };
obj.a = 2; // Mutates `obj`
こうすることができます:
const obj = Immutable.Map({ a: 1 });
obj.set('a', 2); // Returns a new object without mutating `obj`
このアプローチは関数型プログラミングのパラダイムに則ったもので、Reduxとの相性がとても良くなっています。実際、reducer関数は引数として渡される状態を変更しない、純粋な関数でなければならず、状態オブジェクトを新しく作って返すようになっています。それではImmutableを使い、このやり方を強制してみましょう。
yarn add immutable
を実行します。
このコードベースではMap
を使うのですが、ESLintとAirbnbの設定はクラス以外に大文字の名前を使うと警告を出します。package.json
のeslintConfig
に以下を追加します:
"rules": {
"new-cap": [
2,
{
"capIsNewExceptions": [
"Map",
"List"
]
}
]
}
これはMap
とList
(この2つのImmutableなオブジェクトはずっと使うことになります)を例外扱いするようESLintルールを変更するものです。この冗長なJSONフォーマットはYarn/NPMによって自動的に行われるもので、残念ながらコンパクトにはできません。
それはさておき、Immutableに戻りましょう:
dog-reducer.js
を以下のように修正します:
import Immutable from 'immutable';
import { MAKE_BARK } from '../actions/dog-actions';
const initialState = Immutable.Map({
hasBarked: false,
});
const dogReducer = (state = initialState, action) => {
switch (action.type) {
case MAKE_BARK:
return state.set('hasBarked', action.payload);
default:
return state;
}
};
export default dogReducer;
初期状態はImmutableのMapを使って作られます。そして新しい状態は、それ以前の状態を変更することのないset()
を使って作られるようになります。
containers/bark-message.js
のmapStateToProps
関数を、.hasBarked
の代わりに.get('hasBarked')
を使うよう修正します:
const mapStateToProps = state => ({
message: state.dog.get('hasBarked') ? 'The dog barked' : 'The dog did not bark',
});
アプリケーションは以前と同様の振る舞いをするはずです。
注意: BabelがImmutableについて100KB制限を超えていると警告する場合, package.json
のbabel
のところに"compact": false
を追加します。
上記のコード片を見ると分かる通り、状態オブジェクトは素のオブジェクト属性dog
を持っており、イミュータブルではありません。これはこれで構わないのですが、イミュータブルオブジェクトしか扱いたくない場合、ReduxのcombineReducers
関数を置き換えるため、redux-immutable
パッケージをインストールできます。
オプション:
yarn add redux-immutable
を実行します。app.jsx
にあるcombineReducers
関数をredux-immutable
からimportしたものに置き換えます。bark-message.js
のstate.dog.get('hasBarked')
をstate.getIn(['dog', 'hasBarked'])
に置き換えます。
アプリにアクションを加えていくにつれて、同じボイラープレートを何度も書き加えていくことになります。redux-actions
パッケージを使えば、ボイラープレートのコードを減らしてくれます。dog-actions.js
ファイルをredux-actions
で簡潔に書き換えることができます。
import { createAction } from 'redux-actions';
export const MAKE_BARK = 'MAKE_BARK';
export const makeBark = createAction(MAKE_BARK, () => true);
redux-actions
は先ほど実装したようなアクションであるFlux Standard Actionモデルを実装したもので、このモデルに従っていればredux-actions
はシームレスに導入できます。
yarn add redux-actions
を忘れずに実行します。
(原文: 10 - Immutable JS and Redux Improvements)