web-dev-qa-db-ja.com

Reduxアプリケーションでコード分割のためのリデューサーを動的にロードする方法

私はReduxに移行します。

私のアプリケーションはたくさんのパーツ(ページ、コンポーネント)から構成されているので、たくさんのリデューサーを作りたいと思います。 Reduxの例では、1つのリデューサーを生成するためにcombineReducers()を使用する必要があることを示しています。

また、私が理解しているように、Reduxアプリケーションは1つのストアを持つべきであり、アプリケーションの起動後に作成されます。店が作成されているとき私は私の減力剤を渡すべきである。アプリケーションがそれほど大きくない場合、これは理にかなっています。

しかし、複数のJavaScriptバンドルを作成した場合はどうなりますか?たとえば、アプリケーションの各ページには独自のバンドルがあります。この場合、一体型減速機は良くないと思います。私はReduxのソースを調べたところ、replaceReducer()関数が見つかりました。それは私が欲しいもののようです。

私は私のアプリケーションの各部分のために結合された減力剤を作成し、私がアプリケーションの部分の間を移動するときにreplaceReducer()を使うことができます。

これは良いアプローチですか?

169
Pavel Teterin

更新日: Twitterのやり方 もご覧ください。

これは完全な答えではありませんが、始めるのに役立つはずです。私は古いレデューサーを捨てていないことに注意してください - 私はただ新しいものを組み合わせリストに加えています。私は古いリデューサーを捨てる理由はありません - あなたがmayにしたいポイントである最大のアプリでさえ、あなたは数千の動的モジュールを持つことはまずないでしょう。あなたのアプリケーションでいくつかの減力剤を切り離してください。

reducers.js

import { combineReducers } from 'redux';
import users from './reducers/users';
import posts from './reducers/posts';

export default function createReducer(asyncReducers) {
  return combineReducers({
    users,
    posts,
    ...asyncReducers
  });
}

store.js

import { createStore } from 'redux';
import createReducer from './reducers';

export default function configureStore(initialState) {
  const store = createStore(createReducer(), initialState);
  store.asyncReducers = {};
  return store;
}

export function injectAsyncReducer(store, name, asyncReducer) {
  store.asyncReducers[name] = asyncReducer;
  store.replaceReducer(createReducer(store.asyncReducers));
}

routes.js

import { injectAsyncReducer } from './store';

// Assuming React Router here but the principle is the same
// regardless of the library: make sure store is available
// when you want to require.ensure() your reducer so you can call
// injectAsyncReducer(store, name, reducer).

function createRoutes(store) {
  // ...

  const CommentsRoute = {
    // ...

    getComponents(location, callback) {
      require.ensure([
        './pages/Comments',
        './reducers/comments'
      ], function (require) {
        const Comments = require('./pages/Comments').default;
        const commentsReducer = require('./reducers/comments').default;

        injectAsyncReducer(store, 'comments', commentsReducer);
        callback(null, Comments);
      })
    }
  };

  // ...
}

これを表現するためのより適切な方法があるかもしれません - 私はただアイデアを示しています。

224
Dan Abramov

これが私が現在のアプリに実装した方法です(GitHub号のDanによるコードに基づいています)。

// Based on https://github.com/rackt/redux/issues/37#issue-85098222
class ReducerRegistry {
  constructor(initialReducers = {}) {
    this._reducers = {...initialReducers}
    this._emitChange = null
  }
  register(newReducers) {
    this._reducers = {...this._reducers, ...newReducers}
    if (this._emitChange != null) {
      this._emitChange(this.getReducers())
    }
  }
  getReducers() {
    return {...this._reducers}
  }
  setChangeListener(listener) {
    if (this._emitChange != null) {
      throw new Error('Can only set the listener for a ReducerRegistry once.')
    }
    this._emitChange = listener
  }
}

エントリバンドルに含まれるリデューサーを渡して、アプリのブートストラップ時にレジストリインスタンスを作成します。

// coreReducers is a {name: function} Object
var coreReducers = require('./reducers/core')
var reducerRegistry = new ReducerRegistry(coreReducers)

それから店と路線を設定するとき、あなたがあなたに減数体レジストリを与えることができる機能を使ってください:

var routes = createRoutes(reducerRegistry)
var store = createStore(reducerRegistry)

これらの関数は次のようになります。

function createRoutes(reducerRegistry) {
  return <Route path="/" component={App}>
    <Route path="core" component={Core}/>
    <Route path="async" getComponent={(location, cb) => {
      require.ensure([], require => {
        reducerRegistry.register({async: require('./reducers/async')})
        cb(null, require('./screens/Async'))
      })
    }}/>
  </Route>
}

function createStore(reducerRegistry) {
  var rootReducer = createReducer(reducerRegistry.getReducers())
  var store = createStore(rootReducer)

  reducerRegistry.setChangeListener((reducers) => {
    store.replaceReducer(createReducer(reducers))
  })

  return store
}

これは、この設定とそのソースで作成された基本的なライブの例です。

また、すべてのレデューサーに対してホットリロードを有効にするために必要な設定についても説明します。

23
Jonny Buchanan

レデューサーストアにレジューサーを注入するモジュールを追加しました。これはRedux Injectorと呼ばれています。 https://github.com/randallknutson/redux-injector

使い方は次のとおりです。

  1. 減力剤を結合しないでください。代わりに、それらを結合することなく、通常どおりに関数の(ネストされた)オブジェクトに配置します。

  2. ReduxからのcreateStoreの代わりにredux-injectorからのcreateInjectStoreを使用してください。

  3. InjectReducerを使って新しい減速機を注入します。

これが一例です。

import { createInjectStore, injectReducer } from 'redux-injector';

const reducersObject = {
   router: routerReducerFunction,
   data: {
     user: userReducerFunction,
     auth: {
       loggedIn: loggedInReducerFunction,
       loggedOut: loggedOutReducerFunction
     },
     info: infoReducerFunction
   }
 };

const initialState = {};

let store = createInjectStore(
  reducersObject,
  initialState
);

// Now you can inject reducers anywhere in the tree.
injectReducer('data.form', formReducerFunction);

完全開示:私はモジュールの作成者です。

6
Randall Knutson

2017年10月現在:

  • Reedux

    あなたの店、あなたのプロジェクトやあなたの習慣に触れることなく、ダンが提案したことを実行します。

他のライブラリもありますが、依存関係が多すぎたり、例が少なく、使い方が複雑で、ミドルウェアと互換性がない場合や、状態管理を書き直す必要がある場合があります。 Reeduxの紹介ページからコピーしたもの:

4
Silviu-Marian

Reduxアプリのモジュレーションを助け、Reducerやミドルウェアを動的に追加/削除することを可能にする新しいライブラリをリリースしました。

ご覧ください https://github.com/Microsoft/redux-dynamic-modules

モジュールには次のような利点があります。

  • モジュールは、アプリケーション全体で、または複数の同様のアプリケーション間で簡単に再利用できます。

  • コンポーネントはそれらが必要とするモジュールを宣言し、redux-dynamic-modulesはそのモジュールがそのコンポーネント用にロードされることを保証します。

  • モジュールはストアに動的に追加/削除することができます。コンポーネントがマウントされたとき、またはユーザーがアクションを実行したとき

特長

  • リデューサー、ミドルウェア、ステートを1つの再利用可能なモジュールにまとめます。
  • Reduxストアへのモジュールの追加と削除はいつでもできます。
  • 含まれているコンポーネントを使用して、コンポーネントがレンダリングされたときに自動的にモジュールを追加する
  • 拡張機能は、redux-sagaやredux-observableなどの一般的なライブラリとの統合を提供します。

シナリオ例

  • あなたはすべてのあなたのリデューサーのためのコードを前もってロードしたくはありません。いくつかのリデューサー用のモジュールを定義し、DynamicModuleLoaderとreact-loadableのようなライブラリを使用して実行時にモジュールをダウンロードして追加します。
  • アプリケーションのさまざまな分野で再利用する必要がある一般的なリデューサー/ミドルウェアがいくつかあります。モジュールを定義し、それをそれらの領域に簡単に含めることができます。
  • あなたは同じような状態を共有する複数のアプリケーションを含むモノレポを持っています。いくつかのモジュールを含むパッケージを作成し、それらをアプリケーション間で再利用します。
1
Code Ninja

これは別の コード分割とレデックスストアです。私の考えではかなりシンプルでエレガントです。私はそれが実用的な解決策を探している人のために非常に役に立つかもしれないと思います。

これ--- store は少し単純化されているので、状態オブジェクトに名前空間(reducer.name)を強制することはありません。あなたの減力剤のための命名規則とそれは問題ないはずです。

0
Maksym Oliinyk