1. 程式人生 > 實用技巧 >React-router4的簡單理解

React-router4的簡單理解

瞭解更多請參閱v4 文件

一.介紹


1.說明

react-router4是React官方推擠路由庫,4是最新版本。和之前版本不相容,瀏覽器和RN均相容React Router 4.0 (以下簡稱 RR4) 已經正式釋出,它遵循React的設計理念,即萬物皆元件。所以RR4 只是一堆提供了導航功能的元件(還有若干物件和方法),具有宣告式(引入即用),可組合性的特點。http://www.jianshu.com/p/e3adc9b5f75c
React開發單頁面應用必備的,踐行路由即元件的概念。核心概念:動態路由,Route,Link,Switch。


2.安裝


npm install react-router-dom --save
Router4使用react-router-dom作為瀏覽器端的路由


3.引用


import {BrowserRouter,Route,Link} from 'react-router-dom'

4.比較react-router 還是 react-router-dom?

在 React 的使用中,我們一般要引入兩個包,react 和 react-dom,那麼 react-router 和 react-router-dom 是不是兩個都要引用呢?
非也,坑就在這裡。他們兩個只要引用一個就行了,不同之處就是後者比前者多出了 <Link> <BrowserRouter> 這樣的 DOM 類元件。
因此我們只需引用 react-router-dom 這個包就行了。當然,如果搭配 redux ,你還需要使用 react-router-redux。

what is the diff between react-router-dom & react-router?

二.基本用法-元件


1.<BrowserRouter>


一個使用了 HTML5 history API 的高階路由元件,保證你的 UI 介面和 URL 保持同步。此元件擁有以下屬性:
a).basename: string
作用:為所有位置新增一個基準URL
使用場景:假如你需要把頁面部署到伺服器的二級目錄,你可以使用 basename 設定到此目錄。

<BrowserRouter basename="/minooo">
    <Link to="
/react" /> </BrowserRouter> // 最終渲染為 <a href="/minooo/react">

b).getUserConfirmation: func
作用:導航到此頁面前執行的函式,預設使用 window.confirm
使用場景:當需要使用者進入頁面前執行什麼操作時可用,不過一般用到的不多。

const getConfirmation = (message, callback) => {
  const allowTransition = window.confirm(message)
  callback(allowTransition)
}
 
<BrowserRouter getUserConfirmation={getConfirmation('Are you sure?', yourCallBack)} />

c).forceRefresh: bool
作用:當瀏覽器不支援 HTML5 的 history API 時強制重新整理頁面。
使用場景:同上。

const supportsHistory = 'pushState' in window.history
<BrowserRouter forceRefresh={!supportsHistory} />

d).keyLength: number
作用:設定它裡面路由的location.key的長度。預設是6。(key的作用:點選同一個連結時,每次該路由下的location.key都會改變,可以通過 key 的變化來重新整理頁面。)
使用場景:按需設定。

<BrowserRouter keyLength={12} />

e).children: node
作用:渲染唯一子元素。
使用場景:作為一個 Reac t元件,天生自帶 children 屬性。

嘗試一下

2.<HashRouter>


Hash history 不支援 location.key 和 location.state。另外由於該技術只是用來支援舊版瀏覽器,因此更推薦大家使用 BrowserRouter,此API不再作多餘介紹。

3.<Route>

<Route> 也許是 RR4 中最重要的元件了,重要到你必須理解它,學會它,用好它。它最基本的職責就是當頁面的訪問地址與 Route 上的 path 匹配時,就渲染出對應的 UI 介面。
<Route> 自帶三個 render method 和三個 props 。
a).render methods 分別是:
①<Route component>
②<Route render>
③<Route children>
每種 render method 都有不同的應用場景,同一個<Route> 應該只使用一種 render method ,大部分情況下你將使用 component。
b).props 分別是:
①match
②location
③history
所有的 render method 無一例外都將被傳入這些 props。
c).component
只有當訪問地址和路由匹配時,一個 React component 才會被渲染,此時此元件接受 route props (match, location, history)。
當使用 component 時,router 將使用 React.createElement 根據給定的 component 建立一個新的 React 元素。這意味著如果你使用行內函數(inline function)傳值給 component將會產生不必要的重複裝載。對於內聯渲染(inline rendering), 建議使用 renderprop。

const User =function(match){
    return <h1>Hello {match.params.username}!</h1>
}
或是es6
const ClassOne = ({ match }) => {
  return <h1>Hello {match.params.username}!</h1>
}
 
<BrowserRouter>
    <div>
        <Link to ='/classOne'>初三(1)班</Link>
        <Route path='/:username' exact component={ClassOne} ></Route>
    </div>
</BrowserRouter>
//Hello classOne!

d).render: func
此方法適用於內聯渲染,而且不會產生上文說的重複裝載問題。

import React from 'react';
import ReactDOM from 'react-dom';
import { createStore ,applyMiddleware} from 'redux'; 
import thunk from 'redux-thunk'; 
import { Provider } from 'react-redux'
import {BrowserRouter,Route,Link} from 'react-router-dom'
import Home from './components/home';
import {counter} from './index.redux';
const store = createStore(counter,applyMiddleware(thunk));
ReactDOM.render(
    (<Provider  store={store}>
        <BrowserRouter>
            <div>
                <Link to ='/home'>主頁</Link>
                <Route path='/home' component={Home}></Route>
            </div>
        </BrowserRouter>
    </Provider>),
    document.getElementById('root')
)

home.js

import React from 'react';
import {BrowserRouter,Route,Link} from 'react-router-dom'
 
class Home extends React.Component {
    render() {
        return (
            <div>
                <Link to="/home/test">主頁:render內聯</Link>
                <Route path="/home/test" render={() => <h3>Route  render</h3>}/>
            </div>
        );
    }
}
export default Home

e).children: func
有時候你可能只想知道訪問地址是否被匹配,然後改變下別的東西,而不僅僅是對應的頁面。

注意:

f).path屬性指定路由的匹配規則
path 匹配:萬用字元
(1):paramName
:paramName匹配URL的一個部分,直到遇到下一個/、?、#為止。這個路徑引數可以通過this.props.params.paramName取出。
(2)()
()表示URL的這個部分是可選的。
(3)*
*匹配任意字元,直到模式裡面的下一個字元為止。匹配方式是非貪婪模式。
(4) **
** 匹配任意字元,直到下一個/、?、#為止。匹配方式是貪婪模式。

<Route path="/hello/:name">
// 匹配 /hello/michael
// 匹配 /hello/ryan
 
<Route path="/hello(/:name)">
// 匹配 /hello
// 匹配 /hello/michael
// 匹配 /hello/ryan
 
<Route path="/files/*.*">
// 匹配 /files/hello.jpg
// 匹配 /files/hello.html
 
<Route path="/files/*">
// 匹配 /files/ 
// 匹配 /files/a
// 匹配 /files/a/b
 
<Route path="/**/*.jpg">
// 匹配 /files/hello.jpg
// 匹配 /files/path/to/file.jpg

此外,URL的查詢字串/foo?bar=baz,可以用this.props.location.query.bar獲取。

g).exact

是完全匹配 的意思,這樣跳轉到到其他頁面的時候就不會顯示此時路由下的內容

h).strict: bool
對路徑末尾斜槓的匹配。如果為 true。path 為 '/one/' 將不能匹配 '/one' 但可以匹配 '/one/two'。
如果要確保路由沒有末尾斜槓,那麼 strict 和exact 都必須同時為 true

嘗試一下

4.<Link>


Link元件用於取代<a>元素,生成一個連結,允許使用者點選後跳轉到另一個路由。它基本上就是<a>元素的React 版本,可以接收Router的狀態。

a).to: string
作用:跳轉到指定路徑
使用場景:如果只是單純的跳轉就直接用字串形式的路徑。

b).to: object
作用:攜帶引數跳轉到指定路徑
作用場景:比如你點選的這個連結將要跳轉的頁面需要展示此連結對應的內容,又比如這是個支付跳轉,需要把商品的價格等資訊傳遞過去。

c).replace: bool
為 true 時,點選連結後將使用新地址替換掉上一次訪問的地址,什麼意思呢,比如:你依次訪問 '/one' '/two' '/three' ’/four' 這四個地址,如果回退,將依次回退至 '/three' '/two' '/one' ,這符合我們的預期,假如我們把連結 '/three' 中的 replace 設為 true 時。依次點選 one two three four 然後再回退會發生什麼呢?會依次退至 '/three' '/one'! 為此我做了個線上 demo,大家可以除錯體會一下 !

// Link元件示例
 
// to為string
<Link to="/about">關於</Link>
 
// to為obj
<Link to={{
  pathname: '/courses',
  search: '?sort=name',
  hash: '#the-hash',
  state: { fromDashboard: true }
}}/>
 
// replace 
<Link to="/courses" replace />

demo

import React from 'react';
import ReactDOM from 'react-dom';
import { createStore ,applyMiddleware} from 'redux'; 
import thunk from 'redux-thunk'; 
import { Provider } from 'react-redux'
import {BrowserRouter,Route,Link} from 'react-router-dom'
import {counter} from './index.redux';
 
const store = createStore(counter,applyMiddleware(thunk));
const Links = () => (
  <nav>
    <Link to="/">Home</Link>
    <Link to="/about">About</Link>
    <Link replace to="/contact">Contact</Link>
    <Link to="/nested">Nested</Link>
  </nav>
)
const Nested = () => (
  <div>
    <Link to="/nested/one">One</Link>
    <Link to="/nested/two">Two</Link>
    <Link replace to="/nested/Three">Three</Link>
    <div>選擇一個點選</div>
    <Route path="/nested/:minooo?" render={({match}) => <h2>URL: {match.params.minooo || 'minooo'}</h2>} />
  </div>
)
 
ReactDOM.render(
    (<Provider  store={store}>
        <BrowserRouter>
              <div>
                  <Links />
                  <Route exact path="/" render={() => <h1>Home</h1>} />
                  <Route path="/about" render={() => <h1>About</h1>} />
                  <Route path="/contact" render={() => <h1>Contact</h1>} />
                  <Route path="/nested" render={Nested} />
            </div>
        </BrowserRouter>
    </Provider>),
    document.getElementById('root')
)

5.<NavLink>

這是 <Link> 的特殊版,顧名思義這就是為頁面導航準備的。因為導航需要有 “啟用狀態”。<NavLink><Link>的一個特定版本, 會在匹配上當前 URL 的時候會給已經渲染的元素新增樣式引數,元件屬性:
a).activeClassName: string
導航選中啟用時候應用的樣式名,預設樣式名為 active
b).activeStyle: object
如果不想使用樣式名就直接寫style
c).exact: bool
若為 true,只有當訪問地址嚴格匹配時啟用樣式才會應用
d).strict: bool
若為 true,只有當訪問地址字尾斜槓嚴格匹配(有或無)時啟用樣式才會應用
e).isActive: func
決定導航是否啟用,或者在導航啟用時候做點別的事情。不管怎樣,它不能決定對應頁面是否可以渲染。函式,判斷連結是否啟用的額外邏輯的功能;

import React from 'react';
import ReactDOM from 'react-dom';
import { createStore ,applyMiddleware} from 'redux'; 
import thunk from 'redux-thunk'; 
import { Provider } from 'react-redux'
import {BrowserRouter,Route,Link,NavLink} from 'react-router-dom'
import {counter} from './index.redux';
require('./assets/css/App.css');
 
const store = createStore(counter,applyMiddleware(thunk));
 
const isActiveFunc = (match, location) => {
  console.log(match,'contact')
  return match
}
/*
1.activeClassName  為高亮的類名
2.activeStyle  直接設定樣式
3.isActive是函式 判斷連結是否啟用的額外邏輯的功能;
*/
const Links = () => (
  <nav>
    <NavLink exact activeClassName="active" to="/">Home</NavLink>
    <NavLink activeStyle={{fontSize:'30px',color: 'green'}} to={{pathname: '/about'}}>About</NavLink>
    <NavLink 
      isActive={isActiveFunc}
      activeClassName="active" 
      to="/contact">Contact</NavLink>
  </nav>
)
 
ReactDOM.render(
    (<Provider  store={store}>
        <BrowserRouter>
              <div>
                  <Links />
                <Route exact path="/" render={() => <h1>Home</h1>} />
                <Route path="/about" render={() => <h1>About</h1>} />
                <Route path="/contact" render={() => <h1>Contact</h1>} />
            </div>
        </BrowserRouter>
    </Provider>),
    document.getElementById('root')
)

6.<Switch>

該元件用來渲染匹配地址的第一個<Route>或者<Redirect>。那麼它與使用一堆route又有什麼區別呢?
<Switch>的獨特之處是獨它僅僅渲染一個路由。相反地,每一個包含匹配地址(location)的<Route>都會被渲染。

在上面的列子上補充:

import React from 'react';
import ReactDOM from 'react-dom';
import { createStore ,applyMiddleware} from 'redux'; 
import thunk from 'redux-thunk'; 
import { Provider } from 'react-redux'
import {BrowserRouter,Route,Link,NavLink,Switch} from 'react-router-dom'
import {counter} from './index.redux';
require('./assets/css/App.css');
 
const store = createStore(counter,applyMiddleware(thunk));
 
const isActiveFunc = (match, location) => {
  console.log(match,'contact')
  return match
}
/*
1.activeClassName  為高亮的類名
2.activeStyle  直接設定樣式
3.isActive是函式 判斷連結是否啟用的額外邏輯的功能;
*/
const Links = () => (
  <nav>
    <NavLink exact activeClassName="active" to="/">Home</NavLink>
    <NavLink activeStyle={{fontSize:'30px',color: 'green'}} to={{pathname: '/about'}}>About</NavLink>
    <NavLink 
      isActive={isActiveFunc}
      activeClassName="active" 
      to="/contact">Contact</NavLink>
  </nav>
)
 
ReactDOM.render(
    (<Provider  store={store}>
        <BrowserRouter>
              <div>
                  <Links />
                  <Switch>
                    <Route exact path="/" render={() => <h1>Home</h1>} />
                    <Route path="/about" render={() => <h1>About</h1>} />
                    <Route path="/contact" render={() => <h1>Contact</h1>} />
                    <Route path="/contact/:add" render={() => <h1>add</h1>} />
                </Switch>
            </div>
        </BrowserRouter>
    </Provider>),
    document.getElementById('root')
)

嘗試一下

7.<Redirect>

<Redirect> 渲染時將導航到一個新地址,這個新地址覆蓋在訪問歷史資訊裡面的本該訪問的那個地址。
a).to: string
重定向的 URL 字串
b).to: object
重定向的 location 物件
c).push: bool
若為真,重定向操作將會把新地址加入到訪問歷史記錄裡面,並且無法回退到前面的頁面。
d).from: string
需要匹配的將要被重定向路徑。

嘗試一下

8.Prompt

當用戶離開當前頁面前做出一些提示。
a).message: string
當用戶離開當前頁面時,設定的提示資訊。

<Prompt message="確定要離開?" />
d).message: func
當用戶離開當前頁面時,設定的回掉函式

<Prompt message={location => (
  `Are you sue you want to go to ${location.pathname}?` 
)} />

c).when: bool
通過設定一定條件要決定是否啟用 Prompt

嘗試一下

三.物件和方法

(1).history


histoty 是 RR4 的兩大重要依賴之一(另一個當然是 React 了),在不同的 javascript 環境中, history 以多種能夠行駛實現了對會話(session)歷史的管理。
我們會經常使用以下術語:

  • "browser history" - history 在 DOM 上的實現,用於支援 HTML5 history API 的瀏覽器
  • "hash history" - history 在 DOM 上的實現,用於舊版瀏覽器。
  • "memory history" - history 在記憶體上的實現,用於測試或非 DOM 環境(例如 React Native)。

history 物件通常具有以下屬性和方法:

  • length: number 瀏覽歷史堆疊中的條目數
  • action: string 路由跳轉到當前頁面執行的動作,分為 PUSH, REPLACE, POP
  • location: object 當前訪問地址資訊組成的物件,具有如下屬性:
  1. pathname: string URL路徑
  2. search: string URL中的查詢字串
  3. hash: string URL的 hash 片段
  4. state: string 例如執行 push(path, state) 操作時,location 的 state 將被提供到堆疊資訊裡,state 只有在 browser 和 memory history 有效。
  • push(path, [state]) 在歷史堆疊資訊里加入一個新條目。
  • replace(path, [state]) 在歷史堆疊資訊裡替換掉當前的條目
  • go(n) 將 history 堆疊中的指標向前移動 n。
  • goBack() 等同於 go(-1)
  • goForward 等同於 go(1)
  • block(prompt) 阻止跳轉

history 物件是可變的,因為建議從<Route>的 prop 裡來獲取 location,而不是從 history.location 直接獲取。這樣可以保證 React 在生命週期中的鉤子函式正常執行,例如以下程式碼:

class Comp extends React.Component {
  componentWillReceiveProps(nextProps) {
    // locationChanged
    const locationChanged = nextProps.location !== this.props.location
 
    // 錯誤方式,locationChanged 永遠為 false,因為history 是可變的
    const locationChanged = nextProps.history.location !== this.props.history.location
  }
}

(2).location


location 是指你當前的位置,將要去的位置,或是之前所在的位置

{
  key: 'sdfad1'
  pathname: '/about',
  search: '?name=minooo'
  hash: '#sdfas',
  state: {
    price: 123
  }
}

在以下情境中可以獲取 location 物件

  • 在 Route component 中,以 this.props.location 獲取
  • 在 Route render 中,以 ({location}) => () 方式獲取
  • 在 Route children 中,以 ({location}) => () 方式獲取
  • 在 withRouter 中,以 this.props.location 的方式獲取

location 物件不會發生改變,因此可以在生命週期的回撥函式中使用 location 物件來檢視當前頁面的訪問地址是否發生改變。這種技巧在獲取遠端資料以及使用動畫時非常有用

componentWillReceiveProps(nextProps) {
  if (nextProps.location !== this.props.location) {
    // 已經跳轉了!
  }
}

可以在不同情境中使用 location:

  • <Link to={location} />
  • <NaviveLink to={location} />
  • <Redirect to={location />
  • history.push(location)
  • history.replace(location)

(3).match

match 物件包含了 <Route path> 如何與 URL 匹配的資訊,具有以下屬性:

  • params: object 路徑引數,通過解析 URL 中的動態部分獲得鍵值對
  • isExact: bool 為 true 時,整個 URL 都需要匹配
  • path: string 用來匹配的路徑模式,用於建立巢狀的 <Route>
  • url: string URL 匹配的部分,用於巢狀的 <Link>


在以下情境中可以獲取 match 物件

  • 在 Route component 中,以 this.props.match獲取
  • 在 Route render 中,以 ({match}) => () 方式獲取
  • 在 Route children 中,以 ({match}) => () 方式獲取
  • 在 withRouter 中,以 this.props.match的方式獲取
  • matchPath 的返回值

當一個 Route 沒有 path 時,它會匹配一切路徑。

四.demo

(1).基本跳轉

BrowserRouteR 包裹整個應用; Router路由對應渲染的元件,可巢狀; Link跳轉專用

import React from 'react';
import ReactDOM from 'react-dom';
import { createStore ,applyMiddleware} from 'redux'; 
import thunk from 'redux-thunk'; 
import { Provider } from 'react-redux'
import {BrowserRouter,Route,Link} from 'react-router-dom'
import App from './App';
import {counter} from './index.redux';
 
const store = createStore(counter,applyMiddleware(thunk));
 
function ClassOne(){
    return <p>初三(1)班:有90個人</p>
}
function ClassTwo(){
    return <p>初三(2)班:有50個人</p>
}
function ClassThree(){
    return <p>初三(3)班:有30個人</p>
}
ReactDOM.render(
    (<Provider  store={store}>
        <BrowserRouter>
            <div>
                <h1>初三年段各個班有多少人</h1>
                <ul>
                    <li>
                        <Link to ='/'>初三(1)班</Link>
                    </li>
                    <li>
                        <Link to ='/classTwo'>初三(2)班</Link>
                    </li>
                    <li>
                        <Link to ='/classThree'>初三(3)班</Link>
                    </li>
                </ul>
                <Route path='/' exact component={ClassOne} ></Route>
                <Route path='/classTwo' component={ClassTwo}></Route>
                <Route path='/classThree' component={ClassThree}></Route>
            </div>
        </BrowserRouter>
    </Provider>),
    document.getElementById('root')
)

(2).url引數,Route元件引數可用冒號標識引數

import React from 'react';
import ReactDOM from 'react-dom';
import { createStore ,applyMiddleware} from 'redux'; 
import thunk from 'redux-thunk'; 
import { Provider } from 'react-redux'
import {BrowserRouter,Route,Link} from 'react-router-dom'
import App from './App';
import {counter} from './index.redux';
 
const store = createStore(counter,applyMiddleware(thunk));
 
function ClassOne(){
    return <p>初三(1)班:有90個人</p>
}
function ClassTwo(){
    return <p>初三(2)班:有50個人</p>
}
function ClassThree(){
    return <p>初三(3)班:有30個人</p>
}
class Test extends React.Component{
    constructor(props){
        super(props)
    }
    render(){
        console.log(this.props)
        return <h2>測試{this.props.match.params.location}</h2>
    }
}
ReactDOM.render(
    (<Provider  store={store}>
        <BrowserRouter>
            <div>
                <h1>初三年段各個班有多少人</h1>
                <ul>
                    <li>
                        <Link to ='/'>初三(1)班</Link>
                    </li>
                    <li>
                        <Link to ='/classTwo'>初三(2)班</Link>
                    </li>
                    <li>
                        <Link to ='/classThree'>初三(3)班</Link>
                    </li>
                </ul>
                <Route path='/' exact component={ClassOne} ></Route>
                <Route path='/:location'  component={Test} ></Route>
            </div>
        </BrowserRouter>
    </Provider>),
    document.getElementById('root')
)

(3).巢狀路由

import React from 'react';
import ReactDOM from 'react-dom';
import { createStore ,applyMiddleware} from 'redux'; 
import thunk from 'redux-thunk'; 
import { Provider } from 'react-redux'
import {BrowserRouter,Route,Link} from 'react-router-dom'
import {counter} from './index.redux';
 
 
const Users = ({ match }) => {
    return (
            <div>
                <Link to ='/users/:userId'>使用者列表</Link>
                <Route path={`${match.url}/:userId`} component={Profile}/>
              </div>
        ) 
}
const Profile = () =>{
    return (
            <div>
                <h4>高必,20歲</h4>
            </div>
        )
}
const store = createStore(counter,applyMiddleware(thunk));
ReactDOM.render(
    (<Provider  store={store}>
        <BrowserRouter>
            <div>
                <Link to ='/users'>使用者主頁</Link>
                <Route  path='/users'  component={Users}></Route>
            </div>
        </BrowserRouter>
    </Provider>),
    document.getElementById('root')
)

(4).Switch小案例

import React from 'react';
import ReactDOM from 'react-dom';
import { createStore ,applyMiddleware} from 'redux'; 
import thunk from 'redux-thunk'; 
import { Provider } from 'react-redux'
import {BrowserRouter,Route,Link,Redirect,Switch} from 'react-router-dom'
import App from './App';
import {counter} from './index.redux';
 
const store = createStore(counter,applyMiddleware(thunk));
 
function ClassOne(){
    return <p>初三(1)班:有90個人</p>
}
function ClassTwo(){
    return <p>初三(2)班:有50個人</p>
}
function ClassThree(){
    return <p>初三(3)班:有30個人</p>
}
class Test extends React.Component{
    constructor(props){
        super(props)
    }
    render(){
        console.log(this.props)
        return <h2>測試{this.props.match.params.location}</h2>
    }
}
ReactDOM.render(
    (<Provider  store={store}>
        <BrowserRouter>
            <div>
                <h1>初三年段各個班有多少人</h1>
                <ul>
                    <li>
                        <Link to ='/'>初三(1)班</Link>
                    </li>
                    <li>
                        <Link to ='/classTwo'>初三(2)班</Link>
                    </li>
                    <li>
                        <Link to ='/classThree'>初三(3)班</Link>
                    </li>
                    
                </ul>
                <Switch>
                    {/*Switch只渲染第一路由 它的子路由是不渲染的*/}
                    <Route path='/' exact component={ClassOne} ></Route>
                    <Route path='/classTwo' component={ClassTwo}></Route>
                    <Route path='/classThree' component={ClassThree}></Route>
                    <Route path='/:location' component={Test}></Route>
                </Switch>
            </div>
        </BrowserRouter>
    </Provider>),
    document.getElementById('root')
)

(5)結合redux一起使用 :登入校驗

效果:沒有登入資訊 統一跳轉login;有登入這顯示“”初三年段各個班有多少人“的頁面

index.js 入口檔案

import React from 'react';
import { connect } from 'react-redux'
import { BrowserRouter, Route, Redirect,Switch,Link} from 'react-router-dom'
import { login} from './auth.redux'
class Login extends React.Component {
    render() {
        return (
            <div>
                { this.props.users.isAuth? <Redirect to='/dashboard' /> : null}
                <h2>你沒有許可權,需要登入才能看</h2>
                <button onClick={this.props.login}>登入</button>
            </div>
        )
    }
}
const mapStateToProps = (state) =>{
    return {users:state.auth}
}
//Connect負責從外部獲取元件需要的引數
Login = connect(mapStateToProps,{login})(Login);
export default Login;

login.js

import React from 'react';
import { connect } from 'react-redux'
import { BrowserRouter, Route, Redirect,Switch,Link} from 'react-router-dom'
import { login} from './auth.redux'
class Login extends React.Component {
    render() {
        return (
            <div>
                { this.props.users.isAuth? <Redirect to='/dashboard' /> : null}
                <h2>你沒有許可權,需要登入才能看</h2>
                <button onClick={this.props.login}>登入</button>
            </div>
        )
    }
}
const mapStateToProps = (state) =>{
    return {users:state.auth}
}
//Connect負責從外部獲取元件需要的引數
Login = connect(mapStateToProps,{login})(Login);
export default Login;

Dashboard.js 主頁

import React from 'react';
import { BrowserRouter, Route, Redirect,Switch,Link} from 'react-router-dom'
import { connect } from 'react-redux'
import { logout} from './auth.redux'
function ClassOne(){
    return <p>初三(1)班:有90個人</p>
}
function ClassTwo(){
    return <p>初三(2)班:有50個人</p>
}
function ClassThree(){
    return <p>初三(3)班:有30個人</p>
}
class Dashboard extends React.Component {
    constructor(props){
        super(props)
    }
    render() {
        const match = this.props.match
        const rediectToLogin = <Redirect to='/login'></Redirect>
        const app = (
                <div>
                    <h2>我的名字是{this.props.users.user},年齡{this.props.users.age}歲</h2>
                    <h1>初三年段各個班有多少人</h1>
                    {this.props.users.isAuth? <button onClick={this.props.logout}>登出</button>:null}
                    <ul>
                        <li>
                            <Link to ={`${match.url}/`} >初三(1)班</Link>
                        </li>
                        <li>
                            <Link to ={`${match.url}/classTwo`}>初三(2)班</Link>
                        </li>
                        <li>
                            <Link to ={`${match.url}/classThree`}>初三(3)班</Link>
                        </li>
                    </ul>
                    <Route path={`${match.url}/`} exact component={ClassOne} ></Route>
                    <Route path={`${match.url}/classTwo`} component={ClassTwo}></Route>
                    <Route path={`${match.url}/classThree`}  component={ClassThree}></Route>
                </div>
            )
        return this.props.users.isAuth ? app : rediectToLogin
    }
}
const mapStateToProps = (state) =>{
    return {users:state.auth}
}
Dashboard = connect(mapStateToProps,{logout})(Dashboard);
export default Dashboard;

auth.redux.js

const LOGIN = 'LOGIN'
const LOGOUT = 'LOGOUT'
const USER_DATA='USER_DATA'
const initState = {
    isAuth:false,
    user:'李雲龍',
    age:20
}
export function auth(state=initState,action){
    // console.log(state,action)
    switch(action.type){
        case LOGIN:
            return {...state, isAuth:true}
        case LOGOUT:
            return {...state, isAuth:false}
        case USER_DATA:
            return {...state, user:action.payload.user,age:action.payload.age}
        default:    
            return state    
    }
}
 
export function login(){
    return {type:LOGIN}
}
export function logout(){
    return {type:LOGOUT}
}

reducer/index.js

/*合併所有reducer 並且返回
combineReducers 這個是整合所有的方法
*/
import { combineReducers } from 'redux'
import {counter} from './../index.redux';
import {auth} from './../auth.redux';
 
export default combineReducers({counter,auth})

五.參考

1.React Router v4 入坑指南

2.初探 React Router 4.0

原文地址:https://blog.csdn.net/gao_xu_520/article/details/81133691