web-dev-qa-db-ja.com

ReduxでImmutable.jsを使用する方法は?

Redux フレームワークは reducers を使用して、アクションに応じてアプリの状態を変更します。

重要な要件は、レデューサーが既存の状態オブジェクトを変更できないことです。新しいオブジェクトを生成する必要があります。

悪い例

import {
    ACTIVATE_LOCATION
} from './actions';

export let ui = (state = [], action) => {
    switch (action.type) {
        case ACTIVATE_LOCATION:
            state.activeLocationId = action.id;
            break;
    }

    return state;
};

良い例

import {
    ACTIVATE_LOCATION
} from './actions';

export let ui = (state = [], action) => {
    switch (action.type) {
        case ACTIVATE_LOCATION:
            state = Object.assign({}, state, {
                activeLocationId: action.id
            });
            break;
    }

    return state;
};

これは、 Immutable.js の適切な使用例です。

50
Gajus

Immutableを使用した良い例

import {
    ACTIVATE_LOCATION
} from './actions';

import { Map } from 'immutable';

const initialState = Map({})

export let ui = (state = initialState, action) => {
  switch (action.type) {
    case ACTIVATE_LOCATION:
      return state.set('activeLocationId', action.id);
    default:
      return state;
  }
};
48

Immutable.jsは、必要に応じて各レデューサーで使用する必要があります。

import {
    ACTIVATE_LOCATION
} from './actions';

import Immutable from 'immutable';

export let ui = (state, action) => {
    switch (action.type) {
        case ACTIVATE_LOCATION:
            state = Immutable
                .fromJS(state)
                .set('activeLocationId', action.id)
                .toJS();
            break;
    }

    return state;
};

ただし、この例には多くのオーバーヘッドがあります。reducerアクションが呼び出されるたびに、JavaScriptオブジェクトをImmutableのインスタンスに変換し、結果のオブジェクトを変更してからJavaScriptオブジェクトに戻す必要があります。

より良いアプローチは、初期状態をImmutableのインスタンスにすることです。

import {
    ACTIVATE_LOCATION
} from './actions';

import Immutable from 'immutable';

let initialState = Immutable.Map([]);

export let ui = (state = initialState, action) => {
    switch (action.type) {
        case ACTIVATE_LOCATION:
            state = state.set('activeLocationId', action.id);
            break;
    }

    return state;
};

この方法では、初期状態をImmutableオンスのインスタンスに変換するだけで済みます。次に、各レデューサーはそれをImmutableのインスタンスとして扱い、Immutableのインスタンスとして行に渡します。問題は、ビューコンテキストに値を渡す前に、状態全体をJavaScriptにキャストする必要があることです。

レデューサーで複数の状態ミューテーションを実行している場合は、 バッチミューテーション.withMutationsを使用することを検討してください。

物事を簡単にするために、 redux-immutable ライブラリを開発しました。 reduxパッケージの同じ名前の関数と同等のcombineReducers関数を提供しますが、初期状態とすべてのリデューサーが Immutable.js オブジェクトで動作することを期待しています。

25
Gajus

突然変異を起こさずに更新を行う簡単な方法を探している場合、私はライブラリを管理しています: https://github.com/substantial/updeep これは、私の意見では、良い方法ですreduxでそれを行います。

updeepを使用すると、通常の(凍結された)オブジェクト階層を操作できるため、構造化を行ったり、ログやデバッガー内のオブジェクトを確認したりできます。また、バッチ更新を可能にする強力なAPIも備えています。必要に応じてオブジェクトを複製するため、大規模なデータセットではImmutable.jsほど効率的ではありません。

以下に例を示します(ただし、READMEの内容を確認してください):

import {
    ACTIVATE_LOCATION
} from './actions';
import u from 'updeep';

export let ui = (state = [], action) => {
    switch (action.type) {
        case ACTIVATE_LOCATION:
            state = u({ activeLocation: action.id }, state);
            break;
    }

    return state;
};
8
Aaron Jensen

受け入れられた答えは受け入れられた答えであってはなりません。不変を使用して状態を初期化し、(前述のように)redux-immutablejsを使用する必要があります

const initialState = Immutable.fromJS({}) // or Immutable.Map({})

const store = _createStore(reducers, initialState, compose(
    applyMiddleware(...middleware),
    window.devToolsExtension ? window.devToolsExtension() : f => f
));

Redux-immutablejsのcomposeReducersを使用するより

追加のヒント:react-router-reduxの使用は非常にうまく機能するため、これを追加する場合は、react-router-reduxのリデューサーを次のように置き換えます。

import Immutable from 'immutable';
import {
    UPDATE_LOCATION
} from 'react-router-redux';

let initialState;

initialState = Immutable.fromJS({
    location: undefined
});

export default (state = initialState, action) => {
    if (action.type === UPDATE_LOCATION) {
        return state.merge({
            location: action.payload
        });
    }

    return state;
};

これをルートレデューサーにインポートするだけです

これはredux-immutablejsのドキュメントにも記載されています

5
Michiel

https://github.com/indexiatech/redux-immutablejs をご覧ください

Redux標準に準拠するcombineReducerとオプションのcreateReducerです。

2
asaf000