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 学习实践的更多相关文章
- [Web 前端] React Router v4 入坑指南
cp from : https://www.jianshu.com/p/6a45e2dfc9d9 万恶的根源 距离React Router v4 正式发布也已经过去三个月了,这周把一个React的架子 ...
- React Router V4发布
React Router V4 正式版发布,该版本相较于前面三个版本有根本性变化,遵循 Just Component 的 API 设计理念. 本次升级的主要变更有: 声明式 Declarative 可 ...
- [React Router v4] Intercept Route Changes
If a user has entered some input, or the current Route is in a “dirty” state and we want to confirm ...
- [React Router v4] Redirect to Another Page
Overriding a browser's current location without breaking the back button or causing an infinite redi ...
- [React Router v4] Render Multiple Components for the Same Route
React Router v4 allows us to render Routes as components wherever we like in our components. This ca ...
- [React Router v4] Conditionally Render a Route with the Switch Component
We often want to render a Route conditionally within our application. In React Router v4, the Route ...
- [React Router v4] Render Catch-All Routes with the Switch Component
There are many cases where we will need a catch-all route in our web applications. This can include ...
- [React Router v4] Render Nested Routes
With React Router v4 the entire library is built as a series of React components. That means that cr ...
- [React Router v4] Parse Query Parameters
React Router v4 ignores query parameters entirely. That means that it is up to you to parse them so ...
- [React Router v4] Use Regular Expressions with Routes
We can use regular expressions to more precisely define the paths to our routes in React Router v4. ...
随机推荐
- POJ1664 计数 DP
题目传送门 http://poj.org/problem?id=1664 设$dp[i][j]$表示$i$个苹果放在$j$个盘子里的总数 $1.$ 当 苹果数 小于 盘子数 $(M < N) ...
- MySQL 基础 —— 数据类型、各种变量
1. 基本数据类型 char:prod_id char(10),括号内的内容表示字符的长度 decimal:十进制,不带参数为整数(四舍五入) text:文本类型,长度不限 2. 日期和时间处理函数 ...
- continue 的理解
continue 一般出现循环体的开始部分,或中间部分,而不可能是结尾(没有必要,正常执行也会退出本次循环): 1. continue 的替代方案 while (true){ if (A || B){ ...
- 洛谷 P2577 [ ZJOI 2005 ] 午餐 —— DP + 贪心
题目:https://www.luogu.org/problemnew/show/P2577 首先,想一想可以发现贪心策略是把吃饭时间长的人放在前面: 设 f[i][j] 表示考虑到第 i 个人,目前 ...
- Linux压缩命令(zip/gz/bz2/tar/tar.gz/tar.bz2)
一.Linux的压缩格式 .zip . gz . bz2 .tar 1..zip格式(Linux和Windows是可以互传的) 压缩命令 语法:zip 文件名.zip 文件名 ------压缩 ...
- CentOS 6.5克隆后eth1与eth0的问题
CentOS 6.5克隆后eth1与eth0的问题 按照安装文档执行以下步骤时: 从克隆出来的虚拟机网卡都会被命名为eth1,而有些程序或者脚本,涉及到网卡的,默认写的是eth0,这时就存在要 ...
- Manacher HDOJ 3068 最长回文
题目传送门 关于求解最长回文子串,有dp做法,也有同样n^2的但只用O(1)的空间,还有KMP,后缀数组?? int main(void) { ) == ) { ); memset (dp, fals ...
- 状态压缩+枚举 POJ 3279 Fliptile
题目传送门 /* 题意:问最少翻转几次使得棋子都变白,输出翻转的位置 状态压缩+枚举:和之前UVA_11464差不多,枚举第一行,可以从上一行的状态知道当前是否必须翻转 */ #include < ...
- Android 性能优化(26)*性能工具之「Batterystats,Battery Historian」Batterystats & Battery Historian Walkthrough
Batterystats & Battery Historian Walkthrough Working with Batterystats & Battery Historian B ...
- [转]Git使用基础篇
http://www.git-scm.com/ https://try.github.io/levels/1/challenges/1 本文转自:http://www.open-open.com/li ...