Before we implement our own react-router route interception, let’s take a look at how vue’s route interception works and what it does:

As the name suggests, the vue-Router provides navigation guards that are primarily used to guard navigation by jump or cancel.

Global guard

You can register a global front guard using router.beforeeach:

const router = new VueRouter({ ... })

router.beforeEach((to, from, next) = > {
  // ...
Copy the code

When a navigation fires, the global front guard is called in the order it was created. The guard is being resolved asynchronously, where the navigation waits until all the guards have resolved.

Here, we can see that vue can listen for all route hops before all route hops in beforeEach. If it complies with the rules, it will jump; if not, it will jump to the position I specified.

Why can’t the React-Router provide the same API? The author replied on Github:

You can do this from within your render function. JSX doesn't need an API for this because it's more flexible.
Copy the code

It basically means:

You can do this in the render function. JSX does not require an API because it is more flexible.Copy the code

Link: React-router Official description of route interception

It can be seen from the author’s reply that he wants react-Router to be flexible and does not want to add too many apis to it. These apis should allow users to implement their own route interception according to their needs. Next, start implementing your own route interception that meets most of your requirements.

React-router Version: 4.0

First, we’ll use react-router-config to write a route configuration as an array:

//routerConfig.js
const routes = [
    {
        path: '/'.component: 'component/app'.routes: [{path: '/asd'.component: 'component/topics'.routes: [{path: '/asd/login'.component: 'component/home'}]}]}]export default routes
Copy the code

The reason for writing it in the way of configuration is that it can intuitively see the route configuration of our whole project, and know where we want to jump to, and what components will be displayed under what position.

You can see that there are two things that I wrote here that are different from the document:

1. I only have one list item in my entire array, which is the only object. 2. My compoent value is a string, not an object or methodCopy the code

The first point is that we may need a root route in the current page. In the root route, we may do some operations such as setting the theme color, displaying the global content, etc. Here, we can do it. The rest is ok to do in his routes.

Second point: the purpose of this is to achieve faster rendering of the route. In normal use, we generally like this:

// False code, for reference only
import A from './a'
{
    path:'/'.component:A
}
Copy the code

If you have 20 or 50 or 100 paths to go to, you need to import all these files every time you load the router.js file. This affects the efficiency of code execution and the speed of page rendering.

The details of how to render will be subdivided below.

And since the whole route is an array, we’re going to do a loop here if we do it in the most cumbersome way possible

<Route path='/'component={a} /> <Route path='/b' component={b} /> <Route path='/c' component={c} /> <Route path='/d' component={d} /> .Copy the code

Obviously, it is not wise to implement a route jump in this way, we want to write a maintainable, readable route.

So, I’ve written a method here to iterate over the array of routes.

//renderRoutesMap.js
import RouterGuard from './routerGuard'
const renderRoutesMap = (routes) = > (
    routes.map((route, index) = > {
        return( <Route key={index} path={route.path} render={props => ( <RouterGuard {... route} {... props} /> )} /> ) }) ) export default renderRoutesMapCopy the code

Here we do a little traversal of the route rendering, our route object, to do a traversal, the key to come!!

RouterGuard is our focus, and in this case, we’re going to do real route interception (navigation guard)!

//routerGuard.js
import React, { Component } from 'react'
import { withRouter } from 'react-router-dom'
import Loadable from 'react-loadable'
import { connect } from 'react-redux'
import renderRoutesMap from './renderRoutesMap'

const mapStateToProps = state= > (state)
const mapDispatchToProps = dispatch= > ({ ...dispatch })

class RouterGuard extends Component {
    constructor(props) {
        super()
    }
    componentWillMount() {
        let { history: { replace }, authorization, location } = this.props
        if (authorization) replace('./login')
        if (location.pathname === '/') replace('./asd')
        console.log('Intercept before route jump'.this.props)
    }
    render() {
        let { component, routes = [] } = this.props
        console.log('Ready to render compoent before'.this.props)
        const LoadableComponent = Loadable({
            loader: (a)= > import(`.. /${component}`),
            loading: (a)= > (
                <span>11111</span>)})return (
            <div>
                <LoadableComponent {. this.props} / >
                {renderRoutesMap(routes)}
            </div>

        )
    }
}

export default withRouter(connect(mapStateToProps, mapDispatchToProps)(RouterGuard))
Copy the code

In this case, it’s actually the code I used in my project, so there will be react-redux, and if you don’t use redux, you can disassemble it yourself.

ComponentWillMount is the lifetime of the React component, which is called before rendering. Here, we can get the parameters in redux, and we can also get the parameters brought by route.

In this case, I’m using authorization, so if my authorization is true, it means I’m not logged in, so go to login.

I also did a redirection operation, such as on the root route, I want to redirect to another address.

In fact, this can achieve real route interception, but, this is not enough, we not only do interception, we also need to do, in addition to interception, but also as fast as possible to render, improve the user experience.

Here, I use a plugin called Loadable, which is used to load higher-order components with dynamically imported components to improve the user experience.

Improt does not support variables, so I use template string to import the component every time I enter the component and prepare render. In this way, no resources are wasted in each rendering and the speed of the first rendering is guaranteed.

Finally, we splicing the route configuration, route array rendering, route component rendering, and a whole React-router route intercept (navigation guard).

//renderRoutes.js
import renderRoutesMap from './renderRoutesMap'
@param {array} routes * @param {object} extraProps = {} extra * @param {object} SwitchProps = {} Switch attribute */
const renderRoutes = ({ routes, extraProps = {}, switchProps = {} }) = > (
    <Router>
        <Switch {. switchProps} >
            {renderRoutesMap(routes)}
        </Switch>
    </Router>
)

export default renderRoutes

//index.js
const router = (a)= > (
    renderRoutes({
        routes: routerConfig
    })
)
export default router
Copy the code

Finally, introduce index.js into the page.

This is the way I use in my study and work. If there is anything wrong, I hope you can point it out. At last, I hope you can give more praises and more attention. Thank you 🙏