4

redux-observable を react-router で使用する場合、ルート変更中に react-router の onEnter フック内のドキュメントの指示に従って、新しいエピックを非同期に追加することは理にかなっていますか?

./epics/index.js

import { BehaviorSubject } from 'rxjs/BehaviorSubject';
import { combineEpics } from 'redux-observable';

import { epic1 } from './epic1'
import { epic2 } from './epic2'

export const epic$ = new BehaviorSubject(combineEpics(epic1, epic2));
export const rootEpic = (action$, store) =>
    epic$.mergeMap(epic =>
        epic(action$, store)
    );

export {
    rootEpic
};

...ルート/SomePath/index.js

import { epic$ } from './../../../../epics/index'
import { epic3 } from './../../../../epics/epic3'

module.exports = {
    path: 'some-path',
    getComponent( location, cb ) {
        require.ensure( [], ( require ) => {
            cb( null, require( './components/SomePath' ) )
        } )
    },
    onEnter() {
        epic$.next(epic3)

    }
}

Rxjs と redux-observable の非常に新しい。1. この場合、 /some-path に移動するたびに epic3 が rootEpic に再度追加されるのでしょうか? 2. rootEpic に追加されたエピックを console.log に記録したい場合、どうすればよいですか?


@JayPhelpsに応じて編集

いくつかの点について説明を求めてもよろしいですか?

  1. registerEpic fn はうまく機能します。私は .distinct() 演算子を使用して、重複した叙事詩の登録の問題に次のように対処していました。

    export const epic$ = new BehaviorSubject(combineEpics(epic1, epic2)).distinct()

これは、怠惰な叙事詩の登録を処理する同様に有効/適切な方法ですか?そうでない場合は、その理由を説明していただけますか?

  1. 私はrequire.ensureとES6インポートを備えたcreate-react-appを使用しています(これらはインポートの異なる方法ですよね?)、基本的に、コードを含むreact-routerのヒュージアプリの例require.ensureをコピーして貼り付けましたが、どこでもそれ以外の場合は、ファイルの先頭で import ステートメントを使用しています。

したがって、エピックと registerEpic fn を上部にインポートすると機能するように見えますが、require.ensure の最初の引数内にパスを配置することも機能するようです。require.ensure AND import ステートメントを使用すると、混乱しますか? ルートが必要とするすべてのものをロードするために require.ensure を使用する場合、そのルートのコンポーネントのネストされた (ルートのない) コンポーネント内のすべての import ステートメントを削除することを意味しますか?

import { epic3 } from './../../epics/epic3'
import { epic4 } from './../../epics/epic4'
import { registerEpic } from './../../epics/index'

module.exports = {
    path: 'my-path',
    getComponent( location, done ) {
        require.ensure( [], ( require ) => {
           registerEpic(addYoutubeEpic)
            registerEpic(linkYourSiteEpic)
            done( null, require( './components/Edit' ) )
        } )
    }
}
  1. 非同期ロード用の 'getComponent' fn でのエピックの登録について - エピックが登録されるまでルートのコンポーネントがレンダリングされないように、ここで同期ロードを行うことが実際には望ましいと思いました。ユーザーがエピックの登録が必要な詳細情報の取得やフォームの送信など、エピックがまだ登録されていないルートで何かを行おうとするとどうなりますか?

  2. エピックを遅延登録するのに適切な、react-router module.export 宣言以外の場所はありますか? 私のプロジェクトの多くのルートはログインを必要としないため、ログインしても常にルートの変更がトリガーされるとは限りません。ただし、ユーザーがログインした後に登録する必要があるエピックがたくさんあります。現在、authReducer でトークン変数を設定することで、本当にばかげた、おそらく間違った方法でそれを行っていますが、関数を呼び出すだけです。新しい叙事詩を登録します。

    './../reducers/authReducer.js'
    
    import { greatEpic } from './../epics/fetchFollowListEpic'
    import { usefulEpic } from './../epics/fetchMyCollectionsEpic'
    // and a few more 
    import { registerEpic } from './../epics/index'
    
    const addEpics = () => {
        registerEpic(greatEpic)
         registerEpic(usefulEpic)
         ...etc
    }
    
    export default function reducer( state = {
        loggingIn: false,   
        loggedIn: false,
        error: '',
    }, action ) {
        switch ( action.type ) {
            case "LOGIN_SEQUENCE_DONE": {
                return {
                    ...state,
                    aid: setAuthToken(action.payload),
                    loginFailed: false,
                    addEpics: addEpics(),
                }
    
            }
    

レデューサーではなく、loginEpic がエピックを登録するのに適した場所になると思います。(あなたのログイン例を github で見つけたので、見覚えがあるかもしれません)。それは正しいですか、それとも私は完全にベースから外れていますか? 私は .concat() が同期であることを知っており、redux が同期であることを知っています。それとも、問題にならないほど急速に物事が進んでいるということですか? それとも、インポートステートメントと、さらに調査する必要があるコード分割をwebpackがどのように処理するかについて、私が持っているいくつかの本質的な誤解のためにのみ機能しますか?

./../epics/loginEpic

export const loginEpic = action$ =>
    action$.ofType("LOGIN")
        .mergeMap(action =>

            Observable.fromPromise(axios.post('webapi/login', action.payload))
                .flatMap(payload =>
                    // Concat multiple observables so they fire sequentially
                    Observable.concat(
                        // after LOGIN_FULILLED
                        Observable.of({ type: "LOGIN_FULFILLED", payload: payload.data.aid }),                   
                        // ****This is where I think I should be registering the epics right after logging in. "ANOTHER_ACTION" below depends upon one of these epics****     
                        Observable.of({type: "ANOTHER_ACTION", payload: 'webapi/following'}),
                        Observable.of({type: "LOGIN_SEQUENCE_DONE"}),
                    )
                )
                .startWith({ type: "LOGIN_PENDING" })
                .takeUntil(action$.ofType("LOGIN_CANCELLED"))
                .catch(error => Observable.of({
                    type: "LOGIN_REJECTED",
                    payload: error,
                    error: true
                }))
        );
4

1 に答える 1

6

この場合、/some-path に移動するたびに、epic3 が rootEpic に再度追加されるのでしょうか?

react-router を使用してコード分割を行っている場合は、実際には必要なエピックをgetComponentフックではなくフック内に追加する必要がありますonEnter。これはgetComponent、コンポーネントだけでなくエピック、require.ensure()リデューサーなど、そのルートに必要なリソースの非同期読み込みがフックによって許可されるためです。元のエントリ 1--そのため、これらのファイルを他の場所にインポートしないでください。インポートしないと、これが無効になります!

大まかに言えば、これはどのように見えるかの例です:

ルート/SomePath/index.js

import { registerEpic } from 'where/ever/this/is/epics/index.js';

export default {
  path: 'some-path',

  getComponent(location, done) {
    require.ensure(['./components/SomePath', 'path/to/epics/epic3'], require => {
      // this is where you register epics, reducers, etc
      // that are part of this separate bundle
      const epic = require('path/to/epics/epic3').epic3;
      registerEpic(epic);

      done(null, require('./components/SomePath'));
    });
  }
};

where/ever/this/is/epics/index.js

import { BehaviorSubject } from 'rxjs/BehaviorSubject';
import { combineEpics } from 'redux-observable';

import { epic1 } from './epic1';
import { epic2 } from './epic2';

const epicRegistry = [epic1, epic2];
const epic$ = new BehaviorSubject(combineEpics(...epicRegistry));

export const registerEpic = (epic) => {
  // don't add an epic that is already registered/running
  if (epicRegistry.indexOf(epic) === -1) {
    epicRegistry.push(epic);
    epic$.next(epic);
  }
};

// your root epic needs to use `mergeMap` on the epic$ so any
// new epics are composed with the others
export const rootEpic = (action$, store) =>
  epic$.mergeMap(epic =>
    epic(action$, store)
  );

既に追加されているエピックを追加していないことを確認する登録機能を作成しました。getComponent そのルートに入るたびにreact-router が呼び出されるため、同じエピックのインスタンスが 2 つ実行されないようにすることが重要です。

ただし、私のコードは、require('path/to/epics/epic3').epic3. epic3 は実際に名前付きプロパティとしてエクスポートされていますか? 私はそう推測しますが、ES2015/ES6モジュールdone(null, require('./components/SomePath'))を使用してそのコンポーネントをエクスポートしている場合、私の腸が正しく動作しない可能性があると私に言っているこれを行っていることに気付きました。export defaultそれが本当なら、実際にはrequire('./components/SomePath').default--noteで見つかり.defaultます ! したがって、私のコードは一般的なアイデアですが、必要なパス、エクスポートされたプロパティなどに特別な注意を払う必要があります。機能しているように見えるので、おそらく設定が異なると思います(または私は間違っています)


  1. rootEpic に追加されたエピックを console.log に記録したい場合、どうすればよいですか?

registerEpic最も簡単なのは、ユーティリティ内にログインすることです。

export const registerEpic = (epic) => {
  // don't add an epic that is already registered/running
  if (epicRegistry.indexOf(epic) === -1) {
    epicRegistry.push(epic);
    console.log(`new epic added: ${epic.name}`);
    epic$.next(epic);
  }
};

ただし、サブジェクトで RxJS.do()オペレーターを使用して、これをより具体的に行うこともできますepic$

export const rootEpic = (action$, store) =>
  epic$
    .do(epic => console.log(`new epic added: ${epic.name || '<anonymous>'}`))
    .mergeMap(epic =>
      epic(action$, store)
    );

ただし、これは<anonymous>最初にログに記録されます。これはcombineEpics(...epicRegistry)、複数のエピックを 1 つの匿名エピックに結合した結果が最初に取得されたものであるためです。

于 2016-10-24T03:49:58.467 に答える