カスタムReduxの中のcombineReducers

4764 ワード

概要
ReduxのcombineReducersは、複数のreducersを容易に組み合わせて、新しいreducerにすることができます.しかし、私達のアプリケーションがますます複雑になるにつれて、combineReducersは私達の需要を満たすことができないかもしれません.Redux公式文書によると、
This helper is just a conventience!You can write your own combineReducers that works differently,or even asemble the state object from the child reducers mandou and write a root reducing function explicity,like you would write function.combineReducersは私たちが使うのに都合がいいだけです.私たちは完全に違ったcombineReducersをカスタマイズして、私たちの特別な需要を満たすことができます.
原理
まずreducerの書き方を思い出します.
const reducer = (oldState, action) => newState;
reducerは、通常の関数であり、2つのパラメータを受け入れる:oldStateおよびaction、そしてnewStateに戻る.複数のreducersを組み合わせるために、私達は通常Reduxが持参するcombineReducersを使って実現します.
const rootReducer = combineReducers({
  key1: key1Reducer,
  key2: key2Reducer
});
まず注意してください.私たちは何を伝えましたか?combineReducersに渡します.
{
  key1: function(state.key1, action) { /*...*/ },
  key2: function(state.key2, action) { /*...*/ },
}
はい、まず考えてみます.combineReducersの処理を経て、私達は何を得ましたか?考えなくてもいいです.明らかに新しいreducerを得ました.この新しいレデューサーはどうですか?
const rootReducer = (oldState, action) => newState;
驚くはずがないです.すべてのreducerがこのように成長しているので、たとえそれが既に組み合わされたreducerであっても、それはこのように長いです.今はcombineReducersが何をしたかを当てるべきですよね?実は一番基本的な形態はこのようです.
function combineReducers(reducers) {
  return function (state, action) { /*...*/ };
}
これは、reducersをパラメータとして受け入れ、標準のreducer関数を返す.
実際には、このステップでcombineReducersをカスタマイズすることができます.私たちは同じような関数を書くことができます.しかし、先にReduxが持っているswitch...caseが何をしたかを見たほうがいいと思います.私たちがカスタマイズしたcombineReducersは元の機能が必要かもしれません.
先ほどあなたを呼んだところを覚えていますか?そうです.これです.
// reducers
{
  key1: function(state.key1, action) { /*...*/ },
  key2: function(state.key2, action) { /*...*/ }
}
combineReducersのプロセスを振り返ってみましょう.store.dispatch(action)がトリガしたとき、すべてのreducersはこのactionに応答して、対応する変化をして、最後に新しいactionに戻ります.上記の構造に対して、私達は実はこのような効果を簡単に書き出すことができます.他の処理も加えられます.
function reCombineReducers(reducers) {
  return function (state, action) {
    switch (action.type) {
      case SP_ACTION:
        return Object.assign({}, state, { /* do something */ });
      default:
        return Object.keys(reducers)
                    .map(k => ({ [k]: reducers[k](state[k], action) }))
                    .reduce((prev, next) => Object.assign({}, prev, next));
    }
  }
}
上記の例はもとのstoreの機能をシミュレートしました.combineReducersに対して特殊な処理をしました.簡単でしょう.
しかし、上記の例では、cobineReducersの機能をシミュレートしましたが、cobineReducersの検査対象の変化の機能が失われました.今のdefault blockでは新たな対象に戻ります.cobineReducersの全部の機能を保留しながら拡張できる方法がありますか?実はとても簡単です.cobineReducersの戻りの関数を利用すればいいです.(liximomomomoさんが上記の例の欠陥を指摘してくれてありがとうございます)
function reCombineReducers(reducers) {
  let fn = combineReducers(reducers);
  return function (state, action) {
    switch (action.type) {
      case SP_ACTION:
        return Object.assign({}, state, { /* do something */ });
      default:
        return fn(state, action);
    }
  }
}
実例
Reduxの原則に従って、異なったreducerは互いに独立しなければならなくて、それらの間はいかなる依存があるべきでありません.この原則は見たところ美しいですが、実際に使うと例外があります.簡単な例として、私が経験した例です.簡単な表を実現することです.まずSP_ACTIONを設計します.
// state
{
  rows: { ... },
  cells: { ... },
  data: { ... }
}
staterowscellsは、いくつかの特定のdata(例えば、actionCHANGE_ROW_PROPSCHANGE_CELL_PROPS)に応答して、それぞれの変化を作り出します.しかし、いくつかの特殊なaction(CHANGE_DATAなど、サービスからデータを取得することに成功したという意味)が発生した時、災難が発生しました.すべてのreducerはGET_TABLE_SUCCESSのこのactionを傍受する必要があります.GET_TABLE_SUCCESSUPDATE_TABLE_SUCCESSなどのREMOVE_TABLE_SUCCESSを追加すると、n個のファイルを再修正します.これは合理的ではありません.なぜ簡単な機能を加えて、こんなに多くの書類を修正する必要がありますか?
この時、私達は自分達の需要を解決するためにカスタムactionが必要です.
function reCombineReducers(reducers) {
  let fn = combineReducers(reducers);
  return function (state, action) {
    switch (action.type) {
      case GET_TABLE_SUCCESS:
      case UPDATE_TABLE_SUCCESS:
        return Object.assign({}, state, action.payload.table);
      case REMOVE_TABLE_SUCCESS:
        return initState;
      default:
        return fn(state, action);
    }
  }
}

const table = reCombineReducers({
  sections,
  suites,
  rows,
  cells,
  toys,
  data,
  logics
})
どうですか?複数のファイルを修正するよりずっと楽ですか?
(終わり)
出典
http://scarletsky.github.io/2...
参考資料
http://redux.js.org/docs/api/...https://github.com/reactjs/re...