最新のJavaScriptまたはReduxモジュールを再利用可能な状態にする方法





最終的なレデューサーのいくつかのインスタンスでリデューサー関数を再利用したい場合、問題に直面します。









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() { // action types constants this.GET_LIST = Symbol('GET_LIST'); this.REMOVE_ITEM = Symbol('REMOVE_ITEM'); } getList = (serviceName) => { return async (dispatch) => { const list = await services[serviceName].get(); dispatch({ type: this.GET_LIST, payload: { list, serviceName, }, }); }; } removeItem = (index) => { return (dispatch) => { dispatch({ type: this.REMOVE_ITEM, payload: { index, }, }); }; } reducer = (state = initialState, action) => { switch (action.type) { case this.GET_LIST: return getListReducer(state, action); case this.REMOVE_ITEM: return removeItemReducer(state, action); default: return state; } } }
      
      





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 list module class import List from './List'; export default { users: new List(), posts: new List(), };
      
      





Reduxモジュールのクラスを作成して再利用し、必要なだけインスタンスを作成します。







src/redux/modules/reducer.js



メインレデューサー



 import { combineReducers } from 'redux'; // required Redux module instances import list from './list/index'; export default combineReducers({ users: list.users.reducer, posts: list.posts.reducer, });
      
      





src/components/ListView.js



を表示するためのReactコンポーネント



src / components / ListView.js
 import * as React from 'react'; import { connect } from 'react-redux'; import { bindActionCreators } from "redux"; // Redux module instances import list from './../redux/modules/list'; class ListView extends React.Component { componentWillMount() { this.props.getList(this.props.serviceName); } render() { return ( <div> <h1>{this.props.serviceName}</h1> <ul> {this.props.list.map((item, i) => <span key={i}> <li style={{ width: 100 }}> {item} <button style={{ float: 'right' }} onClick={() => this.props.removeItem(i)}>x</button> </li> </span>) } </ul> <button onClick={() => this.props.getList(this.props.serviceName)}>Update</button> </div> ); } } const mapStateToProps = (state, { serviceName }) => ({ ...state[serviceName], }); const mapDispatchToProps = (dispatch, { serviceName }) => ({ ...bindActionCreators({ ...list[serviceName]}, dispatch), }); export default connect(mapStateToProps, mapDispatchToProps)(ListView);
      
      





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モジュールをより便利に再利用できます。 このドキュメントのリポジトリの問題セクションで批判と提案を聞いてうれしいです。








All Articles