4

私はここで説明されている「registerEpic」ユーティリティです: Is it an effective practice to add new epics lazily within react-router onEnter hooks?

私たちのコードは等形である必要がありますが、サーバー側では、アクションが最初にトリガーされ、すべてがうまくいきます。ただし、アクションが 2 回トリガーされると、エピックはそのアクションの 2 つのコピーを取得するようです。これが私のコードです:

export const fetchEpic = (action$, store) =>
  action$.ofType("FETCH")
    .do((action) => console.log('doing fetch', action.payload))
    .mergeMap(({meta:{type}, payload:[url, options = {}]}) => {

            let defaultHeaders = {
                'Accept': 'application/json',
                'Content-Type': 'application/json'
            };

            options.headers = {...defaultHeaders, ...options.headers};

            let request = {
                url,
                method: 'GET',
                responseType: 'json',
                ...options
            };

            //AjaxObservables are cancellable... that's why we use them instead of fetch.  Promises can't be cancelled.

            return AjaxObservable.create(request)
                .takeUntil(action$.ofType(`${type}_CANCEL`))

                .map(({response: payload}) => ({type, payload}))
                .catch(({xhr:{response: payload}}) => (Observable.of({type, payload, error: true})));

        }
    );

 registerEpic(fetchEpic);

したがって、このアクション (サーバー側) をトリガーするページに初めてヒットすると、すべてが正常に機能し、コンソールで「フェッチを実行しています」というメッセージが表示されます。

ただし、ページを更新すると、これらのコンソール メッセージのうち 2 つが生成され、結果のアクションはトリガーされません。

私は壮大なレジストリに「クリア」機能を追加しましたが、おそらく私は完全に初心者であり、完全には理解していません。これが私のミドルウェアです:

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

        if (epic$ !== null) { //this prevents the observable from being used before the store is created.
            epic$.next(epic);
        }
    }
};

export const unregisterEpic =(epic) => {
    const index = epicRegistry.indexOf(epic);
    if(index >= 0) {
        epicRegistry.splice(index, 1);
    }
}

export const clear = () => {
    epic$.complete();
    epic$ = new BehaviorSubject(combineEpics(...epicRegistry));

}

export default () => {

    if (mw === null) {
        epic$ = new BehaviorSubject(combineEpics(...epicRegistry));
        const rootEpic = (action$, store) =>
            epic$.mergeMap(epic => epic(action$, store));

        mw = createEpicMiddleware(rootEpic);
    }

    return mw;
};
4

1 に答える 1

1

epicMiddleware.replaceEpicどこにも見えない?ミドルウェア内で現在実行中のエピックを置き換えない場合、アクションを実行していないことを真に保証することは非常に困難です。これは、ここで起こっていることとまったく同じように聞こえます。

redux-observable (または redux-saga を含む任意の非同期ミドルウェア) を使用してサーバー側でレンダリングを行うのは、何らかの規則を作成する必要があるため困難です。たとえば、アプリの起動時、叙事詩がなんらかの副作用を開始した場合、いつ「それで十分だ」と言い、時期尚早にそれを打ち切りますか? まったく非同期に何かをさせますか? エピックがすべての副作用を真に終わらせることをどのように適切に保証しますか? ミドルウェアがエピックへのサブスクライブを停止する可能性がありますが、サブスクライバーを適切にチェーンしないため、何らかの副作用が続きます。

これが、redux-observable で SSR を実行する方法をまだ文書化していない理由です。これは、「それを実行する」コードを作成しましたが、選択に自信がないためです。

どのように処理しようと決めたとしても、最終的に呼び出しepicMiddleware.replaceEpic(nextRootEpic)て、ミドルウェアが前のルート エピックへのサブスクライブを停止するようにしたい場合があります。

サーバー側でページをレンダリングする際にエピックに非同期で何もさせたくない場合は、ページを同期的にレンダリングし、その直後にルート エピックを同じルート エピックに再度置き換えます。つまり、まったく同じルート エピックを再び渡すことはほぼ確実であり、エピック ツリーを効果的に「再起動」します。

実際の SSR コードを投稿したい場合、たとえば React と react-router を使用している場合、それはすべてあなたのmatch({ routes, location: req.url } ...etc)ものになります。


多くの人が、 SSR は副作用を起こすべきではなく、クライアントに任せるべきであるという意見を持っていることに注意してください。したがって、React などの redux-observable の場合、AJAX 呼び出しはありません。つまり、(サーバー側で実行されない) まで副作用を引き起こすアクションをディスパッチしないことを意味しますcomponentDidMount。これは主に、最初の HTML をできるだけ早く送信せず、JS を脇に置いておかないため、多くの点で SSR のパフォーマンス上の利点が損なわれるためです。ただし、一般的に言及されていないのは、何らかの動的データを取得しない場合、アプリに SEO のメリットがあまりないことは明らかです。これはあなたが決定しなければならないトレードオフであり、あるいは両方を組み合わせて使用​​することもできます。

于 2016-11-09T20:58:27.930 に答える