Наблюдаемые за сокращением множественные действия после 1-й отправки

Я - утилита registerEpic, описанная здесь: Является ли эффективной практикой ленивое добавление новых эпиков внутри перехватчиков response-router onEnter?

Наш код должен быть изоморфным, но на стороне сервера действие запускается в первый раз, и все в порядке. Однако во второй раз, когда действие запускается, кажется, что эпос получает 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;
};

person Ben    schedule 04.11.2016    source источник


Ответы (1)


Я нигде не вижу epicMiddleware.replaceEpic? Если вы не замените запущенный в данный момент эпик внутри промежуточного программного обеспечения, действительно сложно действительно гарантировать, что он не предпринимает никаких действий - что звучит в точности так, как здесь происходит.

Выполнение рендеринга на стороне сервера с помощью redux-observable (или любого асинхронного промежуточного программного обеспечения, включая redux-saga) сложно, потому что вам нужно создать какое-то соглашение. например когда ваше приложение загружается, если в эпопее начинается какой-то побочный эффект, когда вы говорите «хватит» и преждевременно отключаете его? Вы позволяете им вообще что-нибудь делать асинхронно? Как правильно гарантировать, что эпическое действительно избавит от всех побочных эффектов? По промежуточного слоя возможно прекратить подписку на эпик, но оно не связывает своих подписчиков должным образом, поэтому в любом случае сохраняется некоторый побочный эффект.

Вот почему мы еще не задокументировали, как делать SSR в режиме Redux-Observable, потому что, хотя мы создали код, который «делает это», мы не уверены в выборе.

Как бы вы ни решили справиться с этим, очень вероятно, что вы в конечном итоге захотите вызвать epicMiddleware.replaceEpic(nextRootEpic), чтобы промежуточное ПО перестало подписываться на предыдущий корневой эпос.

Если вы не хотите, чтобы эпики выполняли что-либо вообще асинхронно во время рендеринга страницы на стороне сервера, вы должны рендерить страницу синхронно, а затем сразу же после замены корневого эпика на тот же самый корневой эпик. То есть вы почти наверняка снова пройдете тот же самый корневой эпос, который эффективно «перезапустит» ваше древо эпосов - но опять же, никаких гарантий, если вы написали некорректный эпос.

Я рад помочь вам уточнить, если вы хотите опубликовать свой реальный код SSR, например если вы используете React и response-router, это будет все, что вам match({ routes, location: req.url } ...etc).


Стоит отметить, что многие люди считают, что SSR не должен вызывать никаких побочных эффектов и оставлять их на усмотрение клиента. Таким образом, никаких вызовов AJAX в случае, наблюдаемом с помощью redux, с чем-то вроде React, это означает, что не отправляйте действия, вызывающие побочные эффекты, до componentDidMount (который не выполняется на стороне сервера). В основном это связано с тем, что во многих отношениях это сводит на нет возможные преимущества SSR в производительности, потому что вы не отправляете исходный HTML как можно скорее, позволяя JS присутствовать вместе. Однако обычно не упоминается, что, если вы не извлекаете какие-либо динамические данные, ваше приложение явно не будет иметь таких преимуществ для SEO. Это компромисс, который вы должны решить, или, возможно, используйте сочетание того и другого.

person jayphelps    schedule 09.11.2016
comment
@ Бен, это ответ на твой вопрос, или нет? - person jayphelps; 17.11.2016