懒惰地在react-router onEnter挂钩中添加新的史诗是一种有效的做法吗?

时间:2016-10-23 10:49:28

标签: reactjs redux redux-observable redux-router

在使用带反应路由器的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

我可以要求澄清几点吗?

  1. registerEpic fn效果很好。我一直在使用.distinct()运算符来解决重复的史诗注册问题,如下所示:

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

  2. 这是处理懒惰史诗注册的同等有效/好方法,如果没有,你能解释一下原因吗?

    1. 我正在使用create-react-app,它有require.ensure和ES6导入(它们是不同的导入方式吗?),基本上,我复制粘贴了react-router's huge-apps example {{1}代码,但在其他地方,我在文件的顶部使用import语句。
    2. 因此,在顶部导入epics和registerEpic fn似乎有效,而将require.ensure第一个参数中的路径放入也似乎有效。如果我使用require.ensure和import语句,它会变得混乱吗?如果我使用require.ensure加载路由需要的一切,这是否意味着我删除该路由组件的嵌套(无路由)组件内的所有import语句?

      require.ensure
      1. 关于在'getComponent'fn中为异步加载注册史诗 - 我认为实际上需要在此处进行同步加载,以便在注册史诗之前不会呈现路径的组件。如果用户试图在路线上做某事,比如获取一些详细信息或提交表格,需要登记史诗,史诗还没有注册,会发生什么?

      2. 是否有任何其他地方的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' ) )
                } )
            }
        }
        
      3. 我认为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(),
                    }
        
                }
        

1 个答案:

答案 0 :(得分:6)

  

在这种情况下,每当我们导航到/ some-path时,epic3会再次添加到rootEpic吗?

如果您正在使用react-router并进行代码拆分,那么您实际上希望在getComponent挂钩中添加必要的史诗,而不是 {{ 1}}钩子。这是因为onEnter挂钩允许异步加载该路由所需的资源,不仅包括组件,还包括任何史诗,缩减器等。如果使用getComponent,这会告诉webpack将这些项拆分为一个单独的捆绑包,因此它们不包含在原始条目中 - 所以不要在其他任何地方导入这些文件,否则你将否定这个!

粗略地说,这可能是一个例子:

路由/ SomePath / index.js

require.ensure()

其中/以往/这个/是/史诗/ 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'));
    });
  }
};

我创建了一个注册功能,确保您不会添加已添加的史诗; 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>

  
      
  1. 如果我想在console.log中将哪些史诗添加到rootEpic中,我该怎么做?
  2.   

最简单的方法是登录.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>的结果,它将多个史诗组合成一个匿名史诗。