reactrouter4路由钩子_React Router v4 & v5 拦截器(钩子)、静态路由、route-view 实现

前提

React Router 再 v3 版本之前 是有 onEnter 钩子函数的,也支持静态路由配置;,但到了 v4 版本后钩子函数被移除,官方说是为了将此提供给开发者,由开发者自由发挥。既然如此我们就只能自己实现,目前网上有很多版本,大多都是差不多的,这里做一个总结并深化一下。同时提供钩子函数或者vue中叫路由守卫和静态化路由配置。

钩子函数实现

钩子函数实现比较简单,只需要包装一下官方的路由即可实现,这里我们允许钩子支持 Promise 异步,就需要一个异步组件;代码如下

异步组件代码

class AsyncBeforeEnter extends React.PureComponent {

constructor(props) {

super(props)

this.state = {

// 注意:此处component和render不会同时使用,同Route中component和render,render方法优先级要高

// 目标组件 同

Component: null,

// 目标组件render 方法 同

render: null,

// 错误信息

error: null,

// 标记异步是否完成

completed: false

}

}

componentDidMount() {

const { beforeEnter, ...props } = this.props

// beforeEnter 钩子函数

const enter = beforeEnter({ ...props })

if (isPromise(enter)) {

// 判断是否是Promise

enter

.then(next => {

this.handleAfterEnter(next)

})

.catch(error => {

console.error(error)

this.setState({ error })

})

} else {

this.handleAfterEnter(enter)

}

}

handleAfterEnter(next) {

// 结果处理

const { route = {}, ...props } = this.props

// 如果结果是null 或者undefined 或者 true : 不做任何处理直接渲染组件

if (next === null || next === undefined || next === true) {

this.completed(route.component, route.render)

return

}

// 返回false:阻止组件的渲染

if (next === false) {

this.completed(null)

return

}

// 返回 string : 跳转的路由,类似http中302状态码

// 这里使用 React Router 的 Redirect 做跳转

if (typeof next === 'string') {

this.completed(null, () => )

return

}

// 返回React 组件

if (typeof next === 'function' || React.isValidElement(next)) {

this.completed(null, () => next)

return

}

// 返回 Object: 如果有 redirect=true 的属性,做跳转

// 否则使用 Route 组件渲染

if (isPlainObject(next)) {

const { redirect, ...nextProps } = next

if (redirect === true) {

this.completed(null, () => )

return

}

this.completed(() => )

return

}

warn(`"${props.location.pathname} => beforeEnter"

hook return values error. expected null? undefined? true? React.Component? HTMLElement? Route props?

route props detail to see

https://reacttraining.com/react-router/web/api/Route

https://reacttraining.com/react-router/web/api/Redirect`

)

// 例外情况 阻止组件的渲染

this.completed(null)

}

/**

* 完成后改变state渲染组件:

* @param component

* @param render

*/

completed(component, render) {

this.setState({ Component: component, render, completed: true, error: null })

}

getExtraProps() {

// 去掉钩子函数,获取其他props

const { loading: Loading, beforeEnter, ...props } = this.props

return { ...props }

}

render() {

const { Component, render, error, completed } = this.state

if (!completed) {

// 未完成

return null

}

// 已完成

if (render && typeof render === 'function') {

return render(this.getExtraProps())

}

return Component ? : null

}

}

带有钩子函数的 Route

将其命名为 PrivateRoute

export default (route) => (

path={route.path}

exact={route.exact}

strict={route.strict}

location={route.location}

sensitive={route.sensitive}

children={route.children}

render={props => {

// beforeEnter

const { beforeEnter, ...nextProps } = route

// 如果有钩子函数,执行带有异步组件

if (route.beforeEnter && typeof route.beforeEnter === 'function') {

return (

beforeEnter={beforeEnter}

route={nextProps}

{...props}

{...extraProps}

/>

)

}

// 直接渲染

return (

route.render && typeof route.render ?

(

route.render({ ...props, ...extraProps, route: nextProps })

) : (

route.component ? (

route={nextProps}

{...props}

{...extraProps}

/>

) : null

)

)

}}

/>

)

使用的时候就可以用该 Route 代替官方的

示例:

check(props) }/>

check(props) }/>

静态化路由配置,并支持钩子函数

静态化路由配置官方页给出了方案,见:react-router-config,本文的静态路由配置也是参考了该实现,并重写了其中的实现,加入钩子函数

静态路由配置

基本的静态路由表如下

// 顶级两个路由

// 一个登录

// 其他需要授权后放回

export default [

{

path: '/example',

key: 'example',

component: Example,

beforeEnter(props) {

if (auth(props.localtion.pathname)) {

return true

}

return '/login'

},

// 子路由

routes: [

{

path: '/example1',

key: 'example1',

component: Example1,

}

]

},

{

path: '/login',

key: 'login',

component: Login

}

]

改写钩子函数使其能渲染静态路由表=>renderRoutes

// renderRoutes

export default (routes, switchProps = {}, extraProps = {}) => {

return routes && routes.length > 0 ? (

{

routes.map((route, i) => (

key={route.key || i}

path={route.path}

exact={route.exact}

strict={route.strict}

location={route.location}

sensitive={route.sensitive}

children={route.children}

render={props => {

// beforeEnter

const { beforeEnter, ...nextProps } = route

if (route.beforeEnter && typeof route.beforeEnter === 'function') {

return (

beforeEnter={beforeEnter}

route={nextProps}

{...props}

{...extraProps}

/>

)

}

return (

route.render && typeof route.render ?

(

route.render({ ...props, ...extraProps, route: nextProps })

) : (

route.component ? (

route={nextProps}

{...props}

{...extraProps}

/>

) : null

)

)

}}

/>

))

}

) : null

}

使用就可以调用 renderRoutes 方法 , 该实例摘自官方示例:

const Root = ({ route }) => (

Root

{/* child routes won't render without this */}

{renderRoutes(route.routes)}

);

const Home = ({ route }) => (

Home

);

const Child = ({ route }) => (

Child

{/* child routes won't render without this */}

{renderRoutes(route.routes, { someProp: "these extra props are optional" })}

);

const GrandChild = ({ someProp }) => (

Grand Child

{someProp}

);

ReactDOM.render(

{/* kick it all off with the root route */}

{renderRoutes(routes)}

,

document.getElementById("root")

);

实现类似 vue-router 里面的 route-view 功能

经过以上的处理,基本的钩子函数和静态路由就算配置完成了;功能虽然完成了,但总感觉使用上有点麻烦;确实,有没有类似 vue-router 中的 route-view 这种的一步到位的呢?好的,安排。。。

这里需要用到 React context 在 v16 以前这是不推荐的,不过现在已经成熟了,可以大胆的用了;如果不知道怎么用和什么原理可以 去这里 补一下知识

这里还有一个很关键的地方,看图划重点:

可以重复使用,内部的值会覆盖外层的值,这样我们就可以多层路由嵌套了;

创建 context

import React from 'react'

const RouteContext = React.createContext([])

// devtool 中使用

RouteContext.displayName = 'RouteViewContext'

export const RouteProvider = RouteContext.Provider

export const RouteConsumer = RouteContext.Consumer

创建 RouteView

import { RouteConsumer } from './context'

import renderRoutes from './renderRoutes'

//RouteView

export default () => {

return (

{/* 使用静态路由渲染 */}

{/* ruotes 由RouteProvider 提供 */}

{routes => renderRoutes(routes)}

)

}

再次改写 renderRoutes, 使其能够渲染下级路由

import { RouteProvider } from './context'

// renderRoutes

export default (routes, switchProps = {}, extraProps = {}) => {

return routes && routes.length > 0 ? (

{

routes.map((route, i) => (

key={route.key || i}

path={route.path}

exact={route.exact}

strict={route.strict}

location={route.location}

sensitive={route.sensitive}

children={route.children}

render={props => {

checkProps(props)

// beforeEnter

const { beforeEnter, ...nextProps } = route

// RouteProvider 提供下级路由所需的数据

if (route.beforeEnter && typeof route.beforeEnter === 'function') {

return (

beforeEnter={beforeEnter}

route={nextProps}

{...props}

{...extraProps}

/>

)

}

return (

{

route.render && typeof route.render ?

(

route.render({ ...props, ...extraProps, route: nextProps })

) : (

route.component ? (

route={nextProps}

{...props}

{...extraProps}

/>

) : null

)

}

)

}}

/>

))

}

) : null

}

使用

在入口js添加代码如下

import { RouteProvider, RouteView } from '../router'

// 静态路由

const routes = [

// 略。。。

]

class App extends React.PureComponent {

// 略。。。

render() {

return (

// 略。。。

// 提供顶层路由即可

// 下级路由 renderRoutes 处理

// 略。。。

)

}

}

export default App

二级路由使用

class Example extends React.PureComponent {

// 略。。。

render() {

// 此处便不需要再提供routes了

// 在 renderRoutes 已经由 RouteProvider 提供了

return (

Example

)

}

}

export default Example

通过以上努力,我们就具备了静态路由、钩子函数、类似 vue-router 中 router-view;

最终的努力的结果:

方便鉴权

路由方便管理

多层级路由一个组件即可实现渲染

参考文章

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值