【问题标题】:Multiple event listeners in Next.js routerNext.js 路由器中的多个事件监听器
【发布时间】:2017-10-16 12:29:06
【问题描述】:

目前next/router 公开了一个单例 API,可以通过以下方式监听其更改:

Router.onRouteChangeStart = myHandler // subscribe

Router.onRouteChangeStart = null // unsubscribe

这带来了几个与架构相关的挑战,因为两个不相关的组件不能同时监听路由状态变化。

基于对https://github.com/zeit/next.js/issues/2033 的讨论,没有计划将next/router 转换为事件发射器/可观察对象。

鉴于此,我们如何在 Next.js 中实现具有共享订阅的路由器?

【问题讨论】:

    标签: javascript reactjs next.js


    【解决方案1】:

    好消息!从 Next.js 的版本开始,6.1.1 将允许多个路由器事件监听器:

    示例

    Router.events.on('routeChangeStart', (url) => {
      console.log('App is changing to: ', url)
    })
    

    文档

    【讨论】:

    • 你将如何提供一个附加到特定路由更改的监听器,例如:在这个特定路由转换完成后,做点什么?
    • Router.events.on('routeChangeStart', (url) => { if (url === 'myURL') { // Do something } })
    • 如何禁用订阅?假设我有一个针对所有路线更改的全局订阅,我只想关闭一个订阅。
    • 哦,不要为这些任务使用路由器事件!使用componentDidMount/componentWillUnmount 订阅。
    • 嗯,我的用例是显示来自父组件的错误弹出窗口。这有意义吗:gist.github.com/avremel/23c07962b79bac7aff5d4b3950d0172c
    【解决方案2】:

    到目前为止,我很满意的解决方案包括在 Observables 中包装 next/router 侦听器方法,并创建一个 HLA,将路由器事件附加到 componentDidMount 上的组件。

    使用 RxJS 的示例实现:

    // I'm using recompose for and rxjs, but you should be able to modify this code easily
    
    
    // 1. sharedRouter.js
    import Router from 'next/router'
    import { Observable } from 'rxjs'
    
    
    export const routeChangeStart$ = Observable.create(
        obs => {
            console.log('route: start')
            Router.onRouteChangeStart = url => {
                obs.next(url)
            }
        }
    ).share() // note the .share() operator,
              // it ensures that we don't reassign Router.onRouteChangeStart
              // every time a new component subscribes to this observable
    
    export const routeChangeComplete$ = Observable.create(
        obs => {
            Router.onRouteChangeComplete = () => {
                console.log('route: complete')
                obs.next()
            }
        }
    ).share()
    
    export const routeChangeError$ = Observable.create(
        obs => {
            Router.onRouteChangeError = () => {
                console.log('route: error')
                obs.next()
            }
        }
    ).share()
    
    // 2. appBar/withRouterEvents.js
    // This one is attached to our AppNav component
    import { lifecycle } from 'recompose'
    import * as SharedRouter from './sharedRouter'
    
    const withRouterEvents = lifecycle({
        componentDidMount(){
            const onStartLoadingSub = Router.routeChangeStart$
                .subscribe(
                    () => {
                        // hide nav
                        // show loading indicator
                    }
                )
    
            const onFinishLoadingSub = Router
                .routeChangeError$
                .merge(Router.routeChangeComplete$)
                .subscribe(
                    () => {
                        // hide loading indicator
                    }
                )
    
            this.subs = [
                onStartLoadingSub,
                onFinishLoadingSub
            ]
        },
    
        componentWillUnmount(){
            if(!Array.isArray(this.subs)) return;
            this.subs.forEach(
                sub => sub.unsubscribe()
            )
        }
    
    })
    
    
    // 3. appBar/index.js
    export default ({
        isNavVisible,
        isLoading,
        children
    }) => <nav className={
        isNavVisible ? 'app-bar' : 'app-bar app-bar--hidden'
    }>  
        <LoadingIndicator isActive={isLoading} />
        {children}
    </nav>
    

    【讨论】:

      猜你喜欢
      • 2020-05-07
      • 1970-01-01
      • 2016-06-25
      • 2018-04-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-03-19
      相关资源
      最近更新 更多