行動すれば次の現実

テック中心の個人ブログ

React+Reduxのアクション名のコンフリクトを避ける方法

React+Reduxのパターンで開発しているとアクション名の衝突を避けるために管理が煩雑になってきます。

例えば、ある機能でINCREMENTというアクション名をすでに使用している場合、他の機能で同じような振る舞いのアクションを定義する場合はINCREMENTという命名を避けなければなりません。

これは、アクション名が衝突するとそれぞれに定義されたreducerが発火してしまい意図しない動作を引き起こしてしまうためです。

そのため、XXX/INCREMENTZZZ/INCREMENTのように機能ごとのprefixをつけてアクション名の衝突を避ける工夫が必要になります。

redux-actionsを使ってアクション名の衝突を避ける方法

アクションの定義にはredux-actionsというライブラリを使用しているケースが多いかと思います。

redux-actionsを使用することで上記のようなアクション名の衝突を避けるような命名をすることが出来ます。

const { increment, decrement } = createActions(
  'INCREMENT', 
  'DECREMENT', 
  { prefix: 'XXX' },
);

createActionsのオプションにprefixを設定することで、アクション名がXXX/INCREMENTXXX/DECREMENTという命名に変更されます。

同様にreducer側で使用するhandleActionsメソッドにおいてもprefixを指定する必要があります。

export default handleActions({
  [actionTypes.INCREMENT]: (prevState, action) => {
    return {
      ...prevState,
      count: count + 1,
    };
  },
  [actionTypes.DECREMENT]: (prevState, action) => {
    return {
      ...prevState,
      count: count - 1,
    };
  },
}, defaultState, { prefix: XXX });

github.com

また、注意点としてredux-sagaを使用している場合、takeEvery等でアクション名のパターンマッチングをしていますので、そこの考慮も必要になります。 redux-actionsとは別のライブラリですので、仕様のギャップは吸収しなければなりません。

export default function* () {
  yield takeEvery(`XXX/${actionTypes.INCREMENT}`), increment);
  yield takeEvery(`XXX/${actionTypes.DECREMENT}`), decrement);
}