最近学习了 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 学习实践的更多相关文章

  1. [Web 前端] React Router v4 入坑指南

    cp from : https://www.jianshu.com/p/6a45e2dfc9d9 万恶的根源 距离React Router v4 正式发布也已经过去三个月了,这周把一个React的架子 ...

  2. React Router V4发布

    React Router V4 正式版发布,该版本相较于前面三个版本有根本性变化,遵循 Just Component 的 API 设计理念. 本次升级的主要变更有: 声明式 Declarative 可 ...

  3. [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 ...

  4. [React Router v4] Redirect to Another Page

    Overriding a browser's current location without breaking the back button or causing an infinite redi ...

  5. [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 ...

  6. [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 ...

  7. [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 ...

  8. [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 ...

  9. [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 ...

  10. [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. ...

随机推荐

  1. Python Tricks(十九)—— switch 的实现

    python 原生语法不支持 switch,体现了 Python 大道至简的设计思路,有时为了避免啰嗦的 if elif等判断语句,我们可以用字典来代替 switch 的各分支,也即建立表达式和操作的 ...

  2. luogu 1121 环状最大两段子段和

    题目大意: 一个序列看做一个环 选两段数使它们和最大 思路: 定义一个dp数组i j 0/1 表示前i个取了连续的j段 0/1表示取不取第i个 但是因为看做一个环 首尾相接的情况可以看做是选三段,其中 ...

  3. 【Beijing WC2012】 冻结

    [题目链接] 点击打开链接 [算法] dist[i][j]表示到达i号城市,使用了j次魔法,所用时间的最小值 那么,dist[i][j]可以转移到dist[k][j+1]和dist[k][j],一边s ...

  4. python 视频逐帧保存为图片

    import cv2 import os def save_img(): video_path = r'F:\test\video1/' videos = os.listdir(video_path) ...

  5. 698C

    Description n个视频,长度为k的缓存,每次询问,每个视频以pi的概率被选,如果不在缓存区则加入,如果缓存区满了,则最先进缓存的出来,问10^100次操作以后每个视频在缓存的概率 Input ...

  6. bzoj3884

    http://www.lydsy.com/JudgeOnline/problem.php?id=3884 拓展欧拉定理 http://blog.csdn.net/Pedro_Lee/article/d ...

  7. 【145】◀▶ .NET Framework类库索引

    C#编程基础: A1 ………… 基础A2 ………… using 关键字A3 ………… as 关键字A4 ………… is 关键字A5 ………… switch 关键字A6 ………… return 语句关键 ...

  8. IDEA UI版本取消Output窗口打印消息的条数的限制

    打开IDEA的安装目录-->进入bin文件夹-->编辑idea.properties文件::修改idea.cycle.buffer.size=1024为idea.cycle.buffer. ...

  9. centos 7 配置php

    对于我们的目的而言,安装 Apache 只需要在 CentOS 命令终端敲入这条命令就行了: $ sudo yum install httpd $ sudo systemctl enable http ...

  10. ASP.NET MVC 导出CSV文件

    ASP.NET MVC   导出CSV文件.直接贴代码 /// <summary> /// ASP.NET MVC导出CSV文件Demo1 /// </summary> /// ...