При использовании redux-observable с react-router имеет ли смысл асинхронно добавлять новые эпики в соответствии с инструкциями в документации здесь внутри хуков onEnter реагирующего маршрутизатора во время изменения маршрута?
./эпикс/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 и наблюдаемый с редукцией. Кажется, это работает, но интересно: 1. Будет ли в этом случае epic3 снова добавляться к rootEpic каждый раз, когда мы переходим к /some-path ? 2. Если бы я хотел в console.log какие эпики были добавлены в rootEpic, как бы я это сделал?
Отредактировано в ответ @JayPhelps
Могу ли я попросить разъяснений по нескольким пунктам?
В реестре Epic fn прекрасно работает. Я использовал оператор .distinct() для решения проблемы регистрации повторяющихся эпиков следующим образом:
export const epic$ = new BehaviorSubject(combineEpics(epic1, epic2)).distinct()
Является ли это в равной степени правильным/хорошим способом обработки ленивой эпической регистрации, и если нет, не могли бы вы объяснить, почему?
- Я использую приложение create-react-app, в котором есть require.ensure, а также импорт ES6 (это разные способы импорта, верно?), и в основном я скопировал и вставил пример огромных приложений реакции-маршрутизатора, в котором был код
require.ensure
, но везде я использую операторы импорта в верхняя часть файла.
Таким образом, кажется, что импорт эпиков и registerEpic fn вверху работает, в то время как размещение путей внутри первого аргумента require.ensure также работает. Не запутается ли это, если я использую операторы require.ensure AND import? Если я использую require.ensure для загрузки ВСЕХ потребностей маршрута, значит ли это, что я удаляю все операторы импорта внутри вложенных (без маршрута) компонентов компонента этого маршрута?
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' ) )
} )
}
}
Что касается регистрации эпиков в 'getComponent' fn для асинхронной загрузки - я подумал, что на самом деле желательно иметь здесь синхронную загрузку, чтобы компонент маршрута не отображался до тех пор, пока эпик не будет зарегистрирован. Что произойдет, если пользователь попытается сделать что-то на маршруте, например, получить подробную информацию или отправить форму, требующую регистрации эпика, а эпик еще не зарегистрирован?
Есть ли какое-либо другое место за пределами объявлений 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() является синхронным, и я знаю, что избыточный код является синхронным — я не уверен, что registerEpics() является синхронным, но использование редюсера для регистрации эпиков КАЖЕТСЯ работает нормально — означает ли это, что registerEpics является синхронным? Или это просто означает, что все происходит так быстро, что это не имеет значения? Или это работает только из-за некоторого существенного непонимания, которое у меня есть об операторах импорта и о том, как 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
}))
);