React-Router 遇到的那些坑——IndexRoute 嵌套问题
问题概述
IndexRoute的设定是,必须为叶节点,而要是作为父级元素,在IndexRoute下面嵌套更多的子节点。也就是这样的写法是不支持的:
<Router>
<IndexRoute>
<IndexRoute></IndexRoute>
</IndexRoute>
<Route></Route>
</Router>
问题还原
而在实际的业务需求中,经常会遇到下面这种情况:当我从这个入口进入:
想要看到
而实际情况是:
需要一层一层进行点击才能得到想要的结果。(图中的是React-Router官方的示例运行后看到的)
(因为实际业务中都用到异步路由,按需加载,所以照着官方的huge-app示例进行修改和描述。看官如果觉得描述的有点混乱,可以下载源码,npm install后npm start,结合页面跳转进行理解。)
尝试解决
我们先用应用IndexRouter,在huge-apps/routes/Course/index.js
中添加getIndexRoute
方法,完整代码如下:
module.exports = {
path: 'course/:courseId',
getChildRoutes(partialNextState, cb) {
require.ensure([], (require) => {
cb(null, [
require('./routes/Announcements'),
require('./routes/Assignments'),
require('./routes/Grades')
])
})
},
getIndexRoute(location, cb) {
require.ensure([], require =>
cb(null, require('./routes/Announcements'))
)
},
getComponent(nextState, cb) {
require.ensure([], (require) => {
cb(null, require('./components/Course'))
})
}
}
从入口进入后,如图
可以看到,上图和第二张图的 url 是一样的,而显示的效果和这个url( localhost:8080/huge-apps/course/0/announcements)的结果是一样的,这就是IndexRoute的基本用法。这个时候,点击Announcements这个按钮,会发现url改变了,这个时候该死的强迫症就来了——明明页面没有改变,为什么 url 里多了一个字段!!与此同时,控制台会报一个Warning:[react-router] Index routes should not have paths
!!
为了解决这个问题,尝试将/Announcement/index.js
的path
去掉,然后需要修改activeClassName
,链接这个路径需要用IndexLink
组件,或者Link
组件加上onlyActiveOnIndex
属性(详见阮一峰React-Router教程(九))。这样的话,用map遍历出来的Link
组件就需要加入若干个判断,比较麻烦。
这个支线问题只是为了解决强迫症,而且上面的解决方案并不够优雅。下面回到主线问题(工期要紧)。这个时候,如果再用同样的方法,在/Announcement/index.js
中使用getIndexRoute
方法,结果发现,怎么也进不到第三张图的页面了,控制台只是说没有匹配到相应的路由(那只是没有修改Link
组件中to的链接导致的,我们想要的是在/huge-apps/course/0
路径下得到第三张图的效果)。
之后想到另一种解决方法,因为IndexRoute
想解决的问题是this.props.children
为undefined
(详见阮一峰React-Router教程(五)),那我们是不是可以写成{this.props.children || <Announcement/>}
呢。
经过实践,是可以实现需求的,但是和antd的面包屑组件结合起来就会有问题。具体问题是:想要显示的面包屑是三级的,但实际只显示了二级面包屑,而且点击一级面包屑后,面包屑变为一级,但是显示的并不改变,虽然这本来就不会改变,但是这样的体验很不好。。
最后,想出用重定向的方式,即解决了强迫症的问题,又满足了需求,与antd的面包屑组件结合也没有冲突。下面贴上代码:
class SomeApp extends React.Component {
redirect() {
if (!this.props.children) {
this.context.router.push('some url')
}
}
componentWillMount() {
this.redirect()
}
componentDidUpdate() {
this.redirect()
}
render() {
return (
<div>
{this.props.children}
</div>
)
}
}
SomeApp.contextTypes = {
router: React.PropTypes.object
}
这里遇到的最后一个坑是关于生命周期的问题,要把重定向函数写在componentWillMount
和componentDidUpdate
两个方法里。
Author: Wang He
Origin: http://wanghewanghe.github.io
Link: http://wanghewanghe.github.io/2016/12/13/React-Router-遇到的那些坑/
本文采用知识共享署名-非商业性使用 4.0 国际许可协议进行许可