在使用带反应路由器的redux-observable时,根据路由更改期间反应路由器的onEnter挂钩内的文档here中的说明异步添加新的史诗是否有意义?
./史诗/ 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.如果我想在console.log中将哪些史诗添加到rootEpic中,我该怎么做?
编辑以回应@JayPhelps
我可以要求澄清几点吗?
registerEpic fn效果很好。我一直在使用.distinct()运算符来解决重复的史诗注册问题,如下所示:
export const epic$ = new BehaviorSubject(combineEpics(epic1, epic2)).distinct()
这是处理懒惰史诗注册的同等有效/好方法,如果没有,你能解释一下原因吗?
因此,在顶部导入epics和registerEpic fn似乎有效,而将require.ensure第一个参数中的路径放入也似乎有效。如果我使用require.ensure和import语句,它会变得混乱吗?如果我使用require.ensure加载路由需要的一切,这是否意味着我删除该路由组件的嵌套(无路由)组件内的所有import语句?
require.ensure
关于在'getComponent'fn中为异步加载注册史诗 - 我认为实际上需要在此处进行同步加载,以便在注册史诗之前不会呈现路径的组件。如果用户试图在路线上做某事,比如获取一些详细信息或提交表格,需要登记史诗,史诗还没有注册,会发生什么?
是否有任何其他地方的react-router module.export声明适合懒惰注册史诗?由于我的项目中的许多路由不需要登录,因此登录并不总是触发路由更改。然而,在用户登录后应该注册一堆史诗。目前,我通过在我的authReducer中设置一个令牌变量以一种非常愚蠢和可能错误的方式来做它,但它只是调用一个函数注册新史诗:
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' ) )
} )
}
}
我认为loginEpic是一个更好的注册史诗的地方,而不是reducer。 (我发现your login example on github所以看起来很熟悉。这是正确的,还是我完全偏离基地?我知道.concat()是同步的,我知道redux是同步的 - 我不确定registerEpics()是否是同步的,但是使用reducer注册史诗SEEMS才能正常工作 - 这是否意味着registerEpics是同步的?或者它只是意味着事情发射得如此之快以至于无关紧要?或者它是否只能起作用,因为我对import语句有一些重要的误解,以及webpack如何处理代码拆分,我还要研究更多?
'./../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(),
}
}
答案 0 :(得分:6)
在这种情况下,每当我们导航到/ some-path时,epic3会再次添加到rootEpic吗?
如果您正在使用react-router并进行代码拆分,那么您实际上希望在getComponent
挂钩中添加必要的史诗,而不是 {{ 1}}钩子。这是因为onEnter
挂钩允许异步加载该路由所需的资源,不仅包括组件,还包括任何史诗,缩减器等。如果使用getComponent
,这会告诉webpack将这些项拆分为一个单独的捆绑包,因此它们不包含在原始条目中 - 所以不要在其他任何地方导入这些文件,否则你将否定这个!
粗略地说,这可能是一个例子:
require.ensure()
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'));
});
}
};
我创建了一个注册功能,确保您不会添加已添加的史诗; react-router会在每次进入该路由时调用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
之类的内容时会做出一些可能出错的假设。你的epic3实际上是作为命名属性导出的吗?我猜是这样的,但我注意到你正在做这个require('path/to/epics/epic3').epic3
,如果你使用done(null, require('./components/SomePath'))
用ES2015 / ES6模块导出那个组件,我的直觉告诉我可能无法正常工作。如果这是真的,它实际上是在export default
找到的 - 请注意require('./components/SomePath').default
!因此,虽然我的代码是一般的想法,你应该特别注意需求路径,导出的属性等。你说它似乎工作,所以也许你的设置是不同的(或者我只是错了呵呵)< / p>
- 如果我想在console.log中将哪些史诗添加到rootEpic中,我该怎么做?
醇>
最简单的方法是登录.default
实用程序:
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>
的结果,它将多个史诗组合成一个匿名史诗。