
最終的なレデューサーのいくつかのインスタンスでリデューサー関数を再利用したい場合、問題に直面します。
Reduxマルチリデューサーのコンセプト
問題
Redux の作成者は次のように述べています。
例として、A、B、Cという名前のアプリケーションで複数のカウンターを追跡するとします。最初のcounter
レデューサーを定義し、 combineReducers
を使用して状態を設定します。
function counter(state = 0, action) { switch (action.type) { case 'INCREMENT': return state + 1; case 'DECREMENT': return state - 1; default: return state; } } const rootReducer = combineReducers({ counterA : counter, counterB : counter, counterC : counter });
残念ながら、このセットアップには問題があります。 combineReducers
は同じアクションで各スライスレデューサーを呼び出すため、 {type : 'INCREMENT'}
をディスパッチすると、実際には3つのカウンター値が1つだけではなく、すべてインクリメントされます。
解決策
この問題を解決するには、reducer関数の特定のバージョンに
タイプのアクションが必要です。
FPソリューション
ダンは、 関数型プログラミングの世界からのソリューションを提供します-高次のレデューサーです。 レデューサーを高次関数( FWP )でラップし、 FWPを介して転送される追加のサフィックス/プレフィックスを使用してアクションのタイプに名前を付けます。 同様のアプローチ(彼は特別な-
を使用してアクションオブジェクトを専門としてい-
)がErik Rasmussenのライブラリで使用されています 。
OOPソリューション
多かれ少なかれ似たアプローチをお勧めしますが、ラッパー、接尾辞/接頭辞、メタキーなどはありません。 解決策のセクションでは、理由に
した言葉を強調しました。 アクションの種類を実際に一意にするとどうなりますか? 会って、 Symbol
。 MDNからのクリッピング:
Symbol()から返されるすべてのシンボル値は一意です。 シンボル値は、オブジェクトプロパティの識別子として使用できます。 これがデータ型の唯一の目的です。
理想ですね。 そして、 オブジェクト指向プログラミングはどこにありますか? OOPを使用すると、最適な方法でコードを整理し、独自のアクションタイプを作成できます。 Redux成分(またはReduxモジュール)の編成方法は 、同じErik Rasmussenの モジュラーReduxに触発されました。
リストを表示するためのReactアプリケーションの例でこのアプローチを使用してみましょう(このドキュメントのリポジトリには、redux devtools chrome拡張機能と統合された実例があります。リポジトリをコピーして、 npm i
およびnpm run start
コマンドをいくつか実行してください)。
️️ 警告 ️️ Symbol
定数は、時間ベースのデバッグ、アクションの記録および再生などのRedux機能にいくつかの制限を課します。 詳細はこちらをご覧ください。 しかし、この問題は簡単に解決されます。
例(リストを表示するためのReactアプリケーション)
Redux list
モジュール
Redux list
モジュール-モジュールのReduxクラスとこのモジュールの必要なインスタンスが配置されているディレクトリ。
src/redux/modules/list/List.js
リストモジュールのReduxクラス
src / redux / modules / list / List.js import * as services from './../../../api/services'; const initialState = { list: [], }; function getListReducer(state, action) { return { ...state, list: action.payload.list, }; } function removeItemReducer(state, action) { const { payload } = action; const list = state.list.filter((item, i) => i !== payload.index); return { ...state, list, }; } export default class List { constructor() {
src / redux / modules / list / List.devtools.ready.js import * as services from './../../../api/services'; const initialState = { list: [], }; function getListReducer(state, action) { return { ...state, list: action.payload.list, }; } function removeItemReducer(state, action) { const { payload } = action; const list = state.list.filter((item, i) => i !== payload.index); return { ...state, list, }; } function actionType(name) { return { type: name, metaType: Symbol(name), }; } export default class List { constructor(prefix) { this.GET_LIST = actionType(`${prefix}/GET_LIST`); this.REMOVE_ITEM = actionType(`${prefix}/REMOVE_ITEM`); } getList = (serviceName) => { return async (dispatch) => { const list = await services[serviceName].get(); const { type, metaType } = this.GET_LIST; dispatch({ payload: { list, serviceName, }, type, metaType, }); }; } removeItem = (index) => { return (dispatch) => { const { type, metaType } = this.REMOVE_ITEM; dispatch({ payload: { index, }, type, metaType, }); }; } reducer = (state = initialState, action) => { switch (action.metaType) { case this.GET_LIST.metaType: return getListReducer(state, action); case this.REMOVE_ITEM.metaType: return removeItemReducer(state, action); default: return state; } } }
️️ 重要 ️️アクションジェネレーターとリデューサーは、プロトタイプではなくクラスインスタンスのメソッドである必要があります。そうでない場合、 this
は失われthis
。
src/redux/modules/list/index.js
-Reduxモジュールインスタンス
Reduxモジュールのクラスを作成して再利用し、必要なだけインスタンスを作成します。
src/redux/modules/reducer.js
メインレデューサー
import { combineReducers } from 'redux';
src/components/ListView.js
を表示するためのReactコンポーネント
src / components / ListView.js import * as React from 'react'; import { connect } from 'react-redux'; import { bindActionCreators } from "redux";
src/App.jsx
-Reactコンポーネントを使用してリストを表示する
src / app.jsx import React, { Component } from 'react'; import logo from './logo.svg'; import './App.css'; import ListView from './components/ListView'; class App extends Component { render() { return ( <div className="App"> <div className="App-header"> <img src={logo} className="App-logo" alt="logo" /> <h2>Try to Redux multireducer</h2> </div> <ListView serviceName="users" /> <ListView serviceName="posts" /> </div> ); } } export default App;
おわりに
したがって、最新のJavaScriptを使用すると、Reduxモジュールをより便利に再利用できます。 このドキュメントのリポジトリの問題セクションで批判と提案を聞いてうれしいです。