LoginSignup
37

More than 5 years have passed since last update.

ReduxにImmutable.JSを適用してみた

Last updated at Posted at 2017-12-12

はじめに

この記事はDMM.com #1 Advent Calendar 2017 の12/13の記事です。

Immutable.JSを使って、React+Redux製WebアプリのReducerをすっきりさせた方法と結果、及び注意点についてまとめました。

目的

Immutable.JSとは、Facebookが開発した、不変であるデータ構造を扱うためのJavaScriptライブラリです。

開発中だったReact+Reduxアプリでは、開発を進めれば進めるほど、ActionとReducerが肥大化し、改修がしづらくなっていました。
(Reducer内にロジックが含まれていたり、冗長な...stateを書く必要があったり…etc。)

今回は、Immutable.JSを使って、Reducerの肥大化を防ぎ、保守性・改修性を上げることを目的とします。

方法

今回は一般的なReact+Reduxアプリのフローに、Modelという概念を追加します。
Modelでは、actionに応じた処理を記述し、immutableなオブジェクトをReducerに返します。
また、今回は Using Immutable.JS with Redux に出来るだけ沿った記述を行いました。

スクリーンショット 2017-12-12 23.12.57.png

Immutable.JSを使う際に注意すること

書き方が独特

覚えてしまえば楽なのですが、Immutable.JS特有の書式を覚える必要があります。
そのため普通のReact+Reduxと比べると学習コストが少し上がります。

パフォーマンスを重要視した記述について

パフォーマンスを重要視するなら以下の記述に注意すべし。

その1

// これでもうごく
state.set('loading', false).set('user', user)

// さいてき
state.withMutations(s => s.set('loading', false).set('user', user))

Immutable.JS は、内部で何度も再編成を行いデータを構築するため、
withMutations を使って再編成が1回で済むような記述をすることが望ましいようです。

その2

// これでもうごく
const mapStateToProps = (state) => ({
    loans: state.get('loans').toJS(),
})

// さいてき
const mapStateToProps = (state) => ({
    loans: state.get('loans'),
})

mapStateToProps でいちいち toJS() をすると、再編成する回数が増え、パフォーマンスがやや下がるようです。。

オブジェクトの構成について

Immutable.JS でラップされたオブジェクトは、普通のオブジェクトと中身や構成がだいぶ異なるので注意。

// 通常のオブジェクト
{
    type: 'test',
    message: 'fuga',
}

// Immutableなオブジェクト
Hoge {
    values: List {
        size: 2,
        __altered: true,
        __hash: undefined,
        __ownerID: undefined,
        _level: 5,
        _origin: 0,
        _root: null,
        _tail: VNode {
            array: ['test', 'fuga'], 
            ownerID: ownerID {}, 
        },
    }
}

値を取り出して使いたかったら toJS() する必要があります。
ただし mapStateToProps() 内で toJS() をするとパフォーマンスが下がります。気にする場合はHigher Order Component を介して toJS() を行うことで回避できます。

結果

コードはどうなったか

例えば、Reducerの初期値が

{
    input: {
        name: 'hoge',
        item: {
            text: 'hello, Immutable.JS!',
            type: 'fuga',
        },
    },
    data: {
        name: 'test',
        boxes: [
            {
                name: 'hoge',
                items: [],
            },
        ],
    },
};

であるとき、ADD_ITEM_IN_BOXというActionが発行された場合はどうなるか?

before

redux-actionshandleActions()を使用した記述をしています

reducer.js
ADD_ITEM_IN_BOX: (state, action) => {
    const { index, item } = action.payload;
    const box = state.data.boxes[index];
    const items = [...box.items, item];
    const newBox = {
        ...box,
        items,
    };
    const newBoxes = [...state.data.boxes];
    newBoxes[index] = newBox;
    return {
        ...state,
        data: {
            ...state.data,
            boxes: newBoxes,
        },
    };
},

after

reducer.js
ADD_ITEM_IN_BOX: (state, action) => {
    return state.addItemInBox(action.payload.index, action.payload.item);
}
model.js
addItemInBox(index, item) {
    const items = this.get('data').get('boxes').get(index).get('items');
    return this.setIn(['data', 'boxes', index, 'items'], items.push(fromJS(item)));
}

なんとコードの行数が半分以下になりました。

保守性はどうなったか

platoという性能可視化ツールで可視化された解析結果を見ると、行数はアプリ全体でおよそ10000行ありましたが、そのうちの約1000行、全体の1割のコード削減に成功しました。

またReducerファイルの保守性はそれぞれ5~20%上がっており、下がっていたものはありませんでした。

Modelファイルの保守性はどれも70~90%台と高いスコアが出ていました。

これらの結果から、肥大化し改修がしづらくなっていたReducerは、Immutable.JSを適用することで改修がしやすくなり、読みやすくなった、といえるでしょう。

反省・感想・今後の展望

Modelいらない説

今回は、異なるactionで処理する内容が同じであることが多かったためModelに処理を委ねていい感じにすることができましたが、
例えば1つのactionにつき処理が全て異なる、という場合は、Modelを作らずとも、Reducer内に記述を直書きしてもよいなーと思いました。(ググるとこちらの例の方が多いです)

超時間かかった

また、今回は開発の途中でImmutable.JSを導入したせいか、完全移行まで物凄く時間がかかってしまいました(Reducer10個、Action100個近くあるアプリで2週間くらい)。
もし取り入れるのであれば、開発の一番最初から取り入れたほうが吉です。

Action Creatorsの圧縮

実はAction Creatorsについても、もっと短く出来るのではと思い挑戦していましたが、redux-saga等のmiddlewareとの兼ね合いで実現できませんでした。アプリの構成にもよりますが、これを実現するのは難しいと思いました。

まとめ

Immutable.JSをReduxに適用することで、コードの改修がしやすくなることが分かりました。
ただ、独特な書式だったり、書き方によってはパフォーマンスが落ちたりするので、扱う際は注意が必要です。

具体的なModelの作り方については今回は省きましたが、別の記事で紹介する予定です。

参考記事

React使い必見! Immutable.jsでReactはもっと良くなる

Immutable.jsでreact+redux環境が楽になりそうな話

Immutable.jsでReduxにModel層を導入しビジネスロジックを集約する

Reduxのパターンとアンチパターン

Using Immutable.JS with Redux

Estado inmutable con Redux e Immutable.js

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
37