react-router v4 学习实践
Posted caihg
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了react-router v4 学习实践相关的知识,希望对你有一定的参考价值。
最近学习了 react-router v4,根据官方 API 文档和网上资源做了一个简单的路由示例。
先用官方的工具 create-react-app 初始化一个 react 项目模板,再根据自己的需要修改。
要实现的路由:
1. 登录页(/login)
2. 主页(/home):一级导航
3. 商品管理(/goods):一级导航
4. 商品列表(/goods/list):二级导航
5. 商品品牌(/goods/brand):二级导航
6. 路由重定向:
(1)未登录时,地址栏输入主域名(localhost:3000),页面重定向到登录页;否则,重定向到主页。
(2)点击一级导航“商品管理”时,重定向到其下的第一个子导航“商品列表”。
(3)退出后,重定向到登录页。
项目结构:
├── app │ ├── public │ │ ├── favicon.ico │ │ ├── index.html │ │ └── manifest.json │ ├── src │ │ ├── assets │ │ │ ├── app.css │ │ │ └── logo.svg │ │ ├── common │ │ │ └── RouteWithSubRoutes.js │ │ ├── modules │ │ │ ├── asideContainer │ │ │ │ └── Goods.js │ │ │ ├── container │ │ │ │ ├── Container.js │ │ │ │ ├── Header.js │ │ │ │ └── Home.js │ │ │ ├── error │ │ │ │ └── NotFound.js │ │ │ ├── goods │ │ │ │ ├── Brand.js │ │ │ │ └── List.js │ │ │ ├── login │ │ │ │ └── Login.js │ │ ├── index.js │ │ ├── Routes.js │ ├── .gitignore │ ├── package-lock.json │ ├── package.json │ └── README.md
路由配置(src/Routes.js):
import React from \'react\' import { BrowserRouter as Router, Switch, Route } from \'react-router-dom\' import RouteWithSubRoutes from \'./common/RouteWithSubRoutes.js\' import NotFound from \'./modules/error/NotFound.js\' import Login from \'./modules/login/Login.js\' import Container from \'./modules/container/Container.js\' import Home from \'./modules/container/Home.js\' import Goods from \'./modules/asideContainer/Goods.js\' import List from \'./modules/goods/List.js\' import Brand from \'./modules/goods/Brand.js\' const routes = [ { path: \'/home\', component: Home }, { path: \'/goods\', component: Goods, children: [ { path: \'/goods/list\', component: List }, { path: \'/goods/brand\', component: Brand } ] } ] export default () => ( <Router> <Switch> <Route path=\'/login\' component={Login} /> <Container> <Switch> {routes.map((route, i) => ( <RouteWithSubRoutes key={i} {...route} /> ))} <Route component={NotFound} /> </Switch> </Container> </Switch> </Router> )
重定向需要用到 Redirect 组件,但是我的经验就是,Redirect 不要与 Route 作为同级兄弟一起使用,否则页面会保持在 Redirect 指定的路由,而不能跳到其它的路由:
this.props.history.push 指定的路由就会无效。
RouteWithSubRoutes 参考的是官方的的示例。它是一个函数,接收一个对象作为参数,并返回一个(子)路由。在这里它用于渲染一级导航。
登录(src/modules/login/Login.js):
import React, { Component } from \'react\' import { Redirect } from \'react-router-dom\' export default class Login extends Component { constructor(props) { super(props) this.state = { loggedIn: localStorage.getItem(\'loggedIn\'), username: \'anonymous\', password: \'123\' } this.onInputChange = this.onInputChange.bind(this) this.onSubmit = this.onSubmit.bind(this); } onInputChange(event) { const target = event.target const name = target.name const value = target.value this.setState({ [name]: value }) } onSubmit(event) { if (this.state.username && this.state.password) { localStorage.setItem(\'loggedIn\', true) localStorage.setItem(\'username\', this.state.username) this.setState({loggedIn: true}) this.props.history.push(\'/home\') } } render() { if (this.state.loggedIn && this.props.location.pathname === \'/login\') { return ( <Redirect to=\'/home\' /> ) } return ( <div className=\'login-wrap\'> <h2>登 录</h2> <div className=\'field-box\'> <label className=\'control-label\'>用户名:</label> <input type=\'text\' name=\'username\' value={this.state.username} onChange={this.onInputChange} /> </div> <div className=\'field-box\'> <label className=\'control-label\'>密 码:</label> <input type=\'password\' name=\'password\' value={this.state.password} onChange={this.onInputChange} /> </div> <div className=\'field-box\'> <label className=\'control-label\'></label> <button type=\'button\' onClick={this.onSubmit}>登 录</button> </div> </div> ) } }
将用户名写入 localStorage,再通过 this.props.history.push(\'/home\') 跳转到主页。
Container组件(src/modules/container/Container.js):
import React, { Component } from \'react\' import { Redirect } from \'react-router-dom\' import Header from \'./Header\' class Container extends Component { constructor() { super() this.state = { loggedIn: localStorage.getItem(\'loggedIn\'), test: \'it is a testing\' } } render() { if (!this.state.loggedIn) { return ( <Redirect to=\'/login\' /> ) } else if (this.props.location.pathname === \'/\') { return ( <Redirect to=\'/home\' /> ) } return ( <div> <Header {...this.state} /> <div className=\'main-layout\'> {this.props.children} </div> </div> ) } } export default Container
判断用户是否登录,再通过 Redirect 重定向到相应的路由。
this.props.children 用于获取 Container 的子组件。
头部(src/modules/container/Header.js):
import React, { Component } from \'react\' import { NavLink, Redirect } from \'react-router-dom\' export default class Header extends Component { constructor(props) { super(props) this.state = { loggedIn: localStorage.getItem(\'loggedIn\') } } onLogout = () => { localStorage.setItem(\'loggedIn\', \'\') this.setState({loggedIn: false}) } render() { if (!this.state.loggedIn) { return ( <Redirect to=\'/login\' /> ) } return ( <header className=\'fixed-top\'> <div className=\'pull-left\'> <h1>管理平台</h1> <NavLink to=\'/home\' exact>主页</NavLink> <NavLink to=\'/goods\'>商品管理</NavLink> </div> <div className=\'pull-right\'> <div className=\'header-info\'> 欢迎您,{localStorage.getItem(\'username\')} <span style={{marginLeft: 10}}>|</span> <a className=\'logout\' onClick={this.onLogout}>退出</a> </div> </div> </header> ) } }
退出后,清空 localStorage 中的 loggedIn,并重定向到登录页
<Redirect to=\'/login\' />
商品管理(src/modules/asideContainer/Goods.js):
import React from \'react\' import { NavLink, Route, Redirect } from \'react-router-dom\' import RouteWithSubRoutes from \'../../common/RouteWithSubRoutes.js\' export default ({ routes, path }) => ( <div> <div className=\'aside-nav\'> <NavLink to="/goods/list">商品列表</NavLink> <NavLink to="/goods/brand">商品品牌</NavLink> </div> { routes.map((route, i) => { return ( <RouteWithSubRoutes key={i} {...route}/> ) }) } <Route exact path=\'/goods\' render={() => ( <Redirect to=\'goods/list\' /> )} /> </div> )
同样用到了 RouteWithSubRoutes, 在这里它用于渲染二级导航。
通过 Route 判断当前页是“商品管理”(exact 用于路由的严格匹配),再用 Redirect 重定向。
注意,当前路由处于 active 状态,用到的是 NavLink 组件;另一个类似功能的组件是 Link,但没有当前 active 状态。
回过头去看看 Header 组件:
<NavLink to=\'/home\' exact>主页</NavLink> <NavLink to=\'/goods\'>商品管理</NavLink>
对于“主页”,添加了 exact 属性,但“商品管理”则没有,为什么?因为当路由跳转到“商品列表”(/goods/list)时,exact 严格匹配 /goods 的结果为 false,模糊匹配的结果才为 true。
更多细节,详见项目内容。
以上是关于react-router v4 学习实践的主要内容,如果未能解决你的问题,请参考以下文章
刷新浏览器时如何使react-router v4:id-path工作?
在 react-router v4 中使用 React IndexRoute
react-router v4 - browserHistory 未定义