LoginSignup
5
3

More than 5 years have passed since last update.

react+reduxで共通action/reducerを作る

Last updated at Posted at 2019-01-31

1. 概要

react+reduxで大規模SPAを構築する時に、画面ごとに定義されているモデルデータをそれぞれのActionで更新しなければならいので、よく似てるモデルデータのやり取りするソースコードをあっちこっちにコピーするのが効率よくない一歩、メンテナンスし難くなるため、成る可くそれを避けたい。

本文はreducerやの共通化のアプローチでそれの解決を目指す。

2. なぜ共通action/reducerが必要なのか?

  • 共通的なUIコンポーネントが複数画面で使われる、あるいは同じ画面で複数回使われる場合、表示用のデータもそれぞれのモデルに持つ必要がある。
  • 該当データの更新処理をそれぞれのaction/reduxを定義するのが、面倒だし、DRYに違反する。
例:都道府県・市区町村入力の共通UIコンポーネントLocationSuggestを使っている場合、全く一緒なソースコードが生まれる
タッチすると都道府県・市区町村のリストが表示され、選択できる。
  • UIコンポーネントLocationSuggest.jsx
  • モデルデータLocationSuggestRecord.js

  • 画面PageALocationSuggestを使っている

// actionA.js
hoge(value) { disptach({type: 'PAGE_A_CHANGE_WORK_ADDRESS_HOGE', value}) }
fuga(value) { disptach({type: 'PAGE_A_CHANGE_WORK_ADDRESS_FUGA', value}) }
// dataA.js
DataA {
  workAddress : new LocationSuggestRecord(),
}
// reducerA.js
function(state, action) {
  switch(action.type) {
    case 'PAGE_A_CHANGE_WORK_ADDRESS_HOGE' :
      return state.setIn['workAddress', 'Hoge'], value);
    case 'PAGE_A_CHANGE_WORK_ADDRESS_FUGA' :
      return state.setIn['workAddress', 'Fuga'], value);
  }
}
  • 画面PageBもLocationSuggest`を使っている
// actionB.js
hoge(value) { disptach({type: 'PAGE_B_CHANGE_WORK_ADDRESS_HOGE', value}) }
fuga(value) { disptach({type: 'PAGE_B_CHANGE_WORK_ADDRESS_FUGA', value}) }
// dataB.js
DataB {
  workAddress : new LocationSuggestRecord(),
}
// reducerB.js
function(state, action) {
  switch(action.type) {
    case 'PAGE_B_CHANGE_WORK_ADDRESS_HOGE' :
      return state.setIn['workAddress', 'Hoge'], value);
    case 'PAGE_B_CHANGE_WORK_ADDRESS_FUGA' :
      return state.setIn['workAddress', 'Fuga'], value);
  }
}

3. action/reducerを共通化する

共通化するとこうなる。

  • アクションlocationSuggestAction.js
// locationSuggestAction.js
hoge(type, value) { disptach({type, commonType: 'HOGE', value}) }
fuga(type, value) { disptach({type, commonType: 'FUGA', value}) }
  • ReducerlocationSuggestReducer.js
// locationSuggestReducer.js
function(state, action) {
  switch(action.commonType) {
    case 'HOGE' :
      return state.setHoge(action.value)
    case 'FUGA' :
      return state.setFuga(action.value)
  }
}
  • 画面PageAcommonType指定で共通アクションlocationSuggestActionを呼び出す
locationSuggestAction.hoge('PAGE_A_CHANGE_WORK_ADDRESS', 'value');
  • 画面PageAreducerAcommonTypeを判断してからで共通recuderlocationSuggestReducerを呼び出す
// reducerA.js
function(state, action) {
  switch(action.type) {
    case 'PAGE_A_CHANGE_WORK_ADDRESS' :
      // 共通reducerを呼び出す
      return state.set('workAddress', locationSuggestReducer(state.get('workAddress'), action));
    case yy :
      return zz
  }
}
  • 画面PageBも同様
locationSuggestAction.hoge('PAGE_B_CHANGE_WORK_ADDRESS', 'value');
  • 画面PageBreducerBも同様
// reducerB.js
function(state, action) {
  switch(action.type) {
    case 'PAGE_B_CHANGE_WORK_ADDRESS' :
      // 共通reducerを呼び出す
      return state.set('workAddress', locationSuggestReducer(state.get('workAddress'), action));
    case yy :
      return zz
  }
}

共通action/reducer特徴の纏め

  • typeを引数として呼び出す時に渡すだけ、動的にアクション関数を生成はしないので、無駄なレンダリングを防げる
  • 今までのアクションとreducer定義のやり方に従っているので、違和感がない
5
3
0

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
5
3