react,react-router,redux+react-redux 構建一個React Demo

建立初始化應用

加速咱們的npm。css

npm install -g cnpm --registry=https://registry.npm.taobao.org

利用create-react-app 建立項目,執行下面建立咱們的demo-react應用:react

cd ......到你本身的路徑
create-react-app reactdemo

我習慣使用VSCode,我用vscode打開這個文件夾,目錄結構以下。git

安裝須要的依賴

咱們的項目須要:shell

react-router:npm

react-router-domjson

reduxredux

react-redux瀏覽器

在VSCode中打開powershell(Ctrl+`)安全

依次安裝cookie

cnpm install react-router --save
cnpm install react-router-dom --save
cnpm install redux --save
cnpm install react-redux --save

打開package.json能夠看到咱們使用的版本:

 

不一樣的版本使用是有一些區別的,尤爲路由使用上

 

建立幾個組件頁面

在src下建立幾個文件夾存放咱們使用的組件

 

在各自文件夾下建立組件js

Login.js

import React,{ Component } from 'react';

//=====組件=====
class Login extends Component {
    
    render() {
        return (
            <div>
                <h3>登陸頁面</h3>
                <div>
                    用戶名<input type="text" />
                </div>
                <div>
                    密碼<input type="text" />
                </div>
                <div>
                    <button onClick={this.goLogin}>登陸</button>
                </div>
            </div>
        );
    }    
    goLogin(){
        alert("開始登陸")
    }
    
    componentDidMount() {
          console.log("Login渲染完畢")
    }    
}

export default Login

 

Home.js

import React, { Component } from 'react';

//=====組件=====
class Home extends Component {    
    render() {
        return (
            <div>
                <h3>主頁</h3>
            </div>
        );
    }    
    componentDidMount() {
          console.log("Home渲染完畢")
    }
}

export default Home

About.js

import React,{ Component } from 'react';

//=====組件=====
class About extends Component {    
    render() {
        return (
            <div>
                <h3>關於咱們</h3>
            </div>
        );
    }
    
    componentDidMount() {
          console.log("About渲染完畢")
    }    
}

export default About

 

News.js

import React,{ Component } from 'react';
//=====組件=====
class News extends Component {    
    constructor(props) {
        super(props);
        // 設置 initial state
        this.state = {
            list: [
                {id:1,title:"a",con:"caaaaaaaaaaaaaaaa"},
                {id:2,title:"b",con:"cbbbbbbbbbbb"},
                {id:3,title:"c",con:"cccccccccccccc"},
                {id:4,title:"d",con:"cddddddddddddd"},
                {id:5,title:"e",con:"ceeeeeeeeeeee"}
            ]
        };
    }
    
    render() {
        return (
            <div>
                <h3>新聞頁面</h3>
                <ul>
                    {
                        this.state.list.map(function(item,i){
                            return <li key={item.id}>
                                <a>{item.title}</a>
                                <span>{item.con}</span>
                            </li>                        
                        })    
                    }
                </ul>
            </div>
        );
    }
    
    componentDidMount() {
          console.log("News渲染完畢")
    }
    
}


export default News

搭載路由

咱們把頁面建立完畢,修改index.js配置路由:

src/index.js:

 

 1 import React from 'react';
 2 import ReactDOM from 'react-dom';
 3 import {BrowserRouter as Router} from 'react-router-dom';
 4 import './index.css';
 5 import App from './App';
 6 import * as serviceWorker from './serviceWorker';
 7 
 8 ReactDOM.render(
 9     <Router>
10         <App />
11     </Router>    
12 , document.getElementById('root'));
13 
14 // If you want your app to work offline and load faster, you can change
15 // unregister() to register() below. Note this comes with some pitfalls.
16 // Learn more about service workers: https://bit.ly/CRA-PWA
17 serviceWorker.unregister();
View Code

App.js完成咱們路由和頁面的使用: 

import React from 'react';
import {Route,  Link} from 'react-router-dom';
import './App.css';
import Login from './components/login/Login.js';
import Home from './components/home/Home.js';
import About from './components/about/About.js';
import News from './components/news/News.js';

function App() {
  return (
    <div className="App">      
      <ul>
        <li>
          <Link to="/">登陸</Link>
        </li>
        <li>
          <Link to="/Home">主頁</Link>
        </li>
        <li>
          <Link to="/About">關於咱們</Link>
        </li>
        <li>
          <Link to="/News">新聞頁面</Link>
        </li>
      </ul>
      <div>
          <Route exact path="/" component={Login}/>
          <Route exact path="/Home" component={Home}/>
          <Route path="/About" component={About}/>
          <Route path="/News" component={News}/>
      </div>
    </div>
  );
}

export default App;

 

咱們預覽頁面,就能夠看到大概了:

npm run start

使用redux

src目錄下建立redux文件:

indexRedux.js(根狀態樹)咱們存放登陸狀態,默認是未登陸:

//reducer
//根狀態樹)咱們存放登陸狀態,默認是未登陸:
var isLogin=false;

function indexRedux(state = isLogin, action) {
    switch (action.type) {
        case "GO_LOGIN":
            //登陸
            return true
        case "OUT_LOGIN":
            //退出登陸
            return false
        default:
              return state
    }
}

export default indexRedux

rootRedux.js(合併全部狀態,對外接口):

import { combineReducers } from 'redux';

//全局reducer
import isLogin from './indexRedux.js'
//子reducer


//合併reducer
var rootRedux = combineReducers({
    isLogin
})

export default rootRedux

index.js使用redux:

import React from 'react';
import ReactDOM from 'react-dom';
import {BrowserRouter as Router} from 'react-router-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';

//redux 
import { createStore } from 'redux';
//react-redux(關聯react和redux)
import { Provider } from 'react-redux';

//reducers 狀態樹state和邏輯操做
import rootRedux from './rootRedux.js'

//建立狀態樹和設置

//生成狀態樹對象
const store = createStore(rootRedux);
ReactDOM.render(
    <Provider store={store}>
        <Router>
            <App />
        </Router>    
    </Provider>
, document.getElementById('root'));

// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister();
View Code

咱們爲news建立reducer,把list放入在reducer中,

 

NewsRedux.js:

//reducer

var newsinit={
    list:[
        {id:1,title:"a",con:"caaaaaaaaaaaaaaaa"},
        {id:2,title:"b",con:"cbbbbbbbbbbb"},
        {id:3,title:"c",con:"cccccccccccccc"},
        {id:4,title:"d",con:"cddddddddddddd"},
        {id:5,title:"e",con:"ceeeeeeeeeeee"}
    ]
};

function NewsRedux(state = newsinit, action) {
    switch (action.type) {
        case "SORT_REVERSE":
            //倒敘顯示
            var arr=state.list;
            var arr2=[];
            for(var i=arr.length-1;i>=0;i--){
                arr2.push(arr[i])
            }
            return Object.assign({},state,{list:arr2})
        default:
              return state
    }
}


export default NewsRedux

News.js移除構造函數的json設置:

import React,{ Component } from 'react';

//=====組件=====
class News extends Component {    
    constructor(props) {
        super(props);        
    }
    
    render() {
        return (
            <div>
                <h3>消息</h3>
                <ul>
                    {
                        this.state.list.map(function(item,i){
                            return <li key={item.id}>
                                <a>{item.title}</a>
                                <span>{item.con}</span>
                            </li>                        
                        })    
                    }
                </ul>
            </div>
        );
    }
    
    componentDidMount() {
          console.log("News渲染完畢")
    }
    
}


export default News

rootRedux.js引入news的reducer:

import { combineReducers } from 'redux';

//全局reducer
import isLogin from './indexRedux.js'
//子reducer
import NewsRedux from './components/news/NewsRedux.js'

//合併reducer
var rootRedux = combineReducers({
    isLogin,
    NewsRedux,
})

export default rootRedux
View Code

利用react-redux連接react組件和redux

咱們先以news的處理做爲操做,首先用react-redux封裝News.js組件:

建立src/news/NewsReactRedux.js:.

import { connect } from 'react-redux';

//=====引入組件=====
import News from './News.js'


//=====react-redux 封裝組件=====

// 哪些 Redux 全局的 state 是咱們組件想要經過 props 獲取的?
//負責輸入邏輯,即將state映射到 UI 組件的參數(props)
function mapStateToProps(state) {
    return {
        list: state.NewsRedux.list
    };
}

// 哪些 action 建立函數是咱們想要經過 props 獲取的?
//負責輸出邏輯,即將用戶對 UI 組件的操做映射成 Action。
function mapDispatchToProps(dispatch) {
    return {
        SORTREVERSE:function(){
            dispatch({type:"SORT_REVERSE"})
        }
    };
}

//封裝傳遞state和dispatch
//把redux的狀態數據和action所有發射給News組件
var NewsReactRedux = connect(mapStateToProps,mapDispatchToProps)(News);

export default NewsReactRedux

 

咱們把redux的狀態數據和action所有發射給News組件,咱們在裏面使用便可:

 News.js

import React,{ Component } from 'react';

//=====組件=====
class News extends Component {    
    constructor(props) {
        super(props);        
    }
    
    render() {
        return (
            <div>
                <h3>消息</h3>
                <ul>
                    {
                        this.props.list.map(function(item,i){
                            return <li key={item.id}>
                                <a>{item.title}</a>
                                <span>{item.con}</span>
                            </li>                        
                        })    
                    }
                </ul>
                <button onClick={this.SORTREVERSE.bind(this)}>倒敘顯示</button>
            </div>
        );
    }
    SORTREVERSE(){
        this.props.SORTREVERSE();
    }
    componentDidMount() {
          console.log("News渲染完畢")
    }
    
}


export default News

App.js使用封裝後的組件News:

 

import React from 'react';
import {Route,  Link} from 'react-router-dom';
import './App.css';
import Login from './components/login/Login.js';
import Home from './components/home/Home.js';
import About from './components/about/About.js';
import NewsReactRedux  from './components/news/NewsReactRedux.js';

function App() {
  return (
    <div className="App">      
      <ul>
        <li>
          <Link to="/">登陸</Link>
        </li>
        <li>
          <Link to="/Home">主頁</Link>
        </li>
        <li>
          <Link to="/About">關於咱們</Link>
        </li>
        <li>
          <Link to="/News">消息</Link>
        </li>
      </ul>
      <div>
          <Route exact path="/" component={Login}/>
          <Route exact path="/Home" component={Home}/>
          <Route path="/About" component={About}/>
          <Route path="/News" component={NewsReactRedux}/>
      </div>
    </div>
  );
}

export default App;
View Code

 能夠運行看看了。

登陸處理

「/「地址就是咱們的登陸頁面,咱們點擊登陸跳轉就能夠了,不過咱們會把用戶的登陸狀態存放在indexRedux.js中,咱們不把這個狀態存如cookie相似的本地,因此 咱們刷新頁面退出便可,咱們只是模擬的處理:

若是存放在了cookie咱們要如何處理,這時候在進入網站咱們能夠調用一個方法去獲取cookie的登陸狀態,無論是什麼咱們都會執行action把redux的登陸狀態改成這個!

由於Login.js要和redux結合,咱們要修改登陸狀態,咱們還要結合router去手動跳轉到主頁,

LoginReactRedux.js:

 

import { connect } from 'react-redux';

//=====引入組件=====
import Login from './Login.js'


//=====react-redux 封裝組件=====

// 哪些 Redux 全局的 state 是咱們組件想要經過 props 獲取的?
function mapStateToProps(state) {
    return {}
}

// 哪些 action 建立函數是咱們想要經過 props 獲取的?
function mapDispatchToProps(dispatch) {
    return {
        GOLOGIN:function(username,password,history){
            console.log("用戶名"+username)
            console.log("密碼"+password)
            setTimeout(function(){
                dispatch({type:"GO_LOGIN"})
                history.push("/Home")
            },1000)
            
        }
    };
}

//封裝傳遞state和dispatch
var LoginReactRedux = connect(mapStateToProps,mapDispatchToProps)(Login);

export default LoginReactRedux

login.js

import React, { Component } from 'react';

//=====組件=====
class Login extends Component {
    
    render() {
        return (
            <div>
                <h3>登陸頁面</h3>
                <div>
                    用戶名<input type="text" ref="username" />
                </div>
                <div>
                    密碼<input type="password" ref="password" />
                </div>
                <div>
                    <button onClick={this.goLogin.bind(this)}>登陸</button>
                </div>
            </div>
        );
    }    
    goLogin(){
        this.props.GOLOGIN(this.refs.username.value,this.refs.password.value,this.props.history);
    }
    
    componentDidMount() {
          console.log("Login渲染完畢")
    }    
}

export default Login

App.js:

import React from 'react';
import {Route,  Link} from 'react-router-dom';
import './App.css';
import LoginReactRedux from './components/login/LoginReactRedux.js';
import Home from './components/home/Home.js';
import About from './components/about/About.js';
import NewsReactRedux  from './components/news/NewsReactRedux.js';

function App() {
  return (
    <div className="App">      
      <ul>
        <li>
          <Link to="/">登陸</Link>
        </li>
        <li>
          <Link to="/Home">主頁</Link>
        </li>
        <li>
          <Link to="/About">關於咱們</Link>
        </li>
        <li>
          <Link to="/News">消息</Link>
        </li>
      </ul>
      <div>
          <Route exact path="/" component={LoginReactRedux}/>
          <Route exact path="/Home" component={Home}/>
          <Route path="/About" component={About}/>
          <Route path="/News" component={NewsReactRedux}/>
      </div>
    </div>
  );
}

export default App;

退出登陸處理

咱們在/Home加一個按鈕就是退出按鈕他和咱們的登陸處理相反:

HomeReactRedux.js:

import { connect } from 'react-redux';

//=====引入組件=====
import Home from './Home.js'


//=====react-redux 封裝組件=====

// 哪些 Redux 全局的 state 是咱們組件想要經過 props 獲取的?
function mapStateToProps(state) {
    return {}
}

// 哪些 action 建立函數是咱們想要經過 props 獲取的?
function mapDispatchToProps(dispatch) {
    return {
        OUTLOGIN:function(history){
            dispatch({type:"OUT_LOGIN"})
            history.push("/")
            
        }
    };
}

//封裝傳遞state和dispatch
var HomeReactRedux = connect(mapStateToProps,mapDispatchToProps)(Home);

export default HomeReactRedux

Home.js:

import React, { Component } from 'react';

//=====組件=====

class Home extends Component {
    
    render() {
        return (
            <div>
                <h3>主頁</h3>
                <div>
                    <button onClick={this.outLogin.bind(this)}>退出登陸</button>
                </div>
            </div>
        );
    }

    outLogin(){
        this.props.OUTLOGIN(this.props.history);
    }
        
    componentDidMount() {
          console.log("Home渲染完畢")
    }    
}

export default Home

記得修改app.js

import React from 'react';
import {Route,  Link} from 'react-router-dom';
import './App.css';
import LoginReactRedux from './components/login/LoginReactRedux.js';
import HomeReactRedux from './components/home/HomeReactRedux.js';
import About from './components/about/About.js';
import NewsReactRedux  from './components/news/NewsReactRedux.js';

function App() {
  return (
    <div className="App">      
      <ul>
        <li>
          <Link to="/">登陸</Link>
        </li>
        <li>
          <Link to="/Home">主頁</Link>
        </li>
        <li>
          <Link to="/About">關於咱們</Link>
        </li>
        <li>
          <Link to="/News">消息</Link>
        </li>
      </ul>      <div>
          <Route exact path="/" component={LoginReactRedux}/>
          <Route exact path="/Home" component={HomeReactRedux}/>
          <Route path="/About" component={About}/>
          <Route path="/News" component={NewsReactRedux}/>
      </div>
    </div>
  );
}

export default App;

 

 

權限處理

顯示級別

咱們的網站在/地址是處在登陸頁面,這時候咱們應該只有登陸框,在進入主頁以後會看到跳轉連接,咱們要獲取咱們的登陸狀態,還控制一些標籤的顯示:

咱們在App.js存放了咱們的導航,咱們只須要對這個組件利用react-redux作一次封裝,拿到狀態,利用style去處理便可:

咱們把導航提出到組件,而且react-redux作封裝,在App.js使用

Navigation.js:

import React, { Component } from 'react';
import {Route,Link} from 'react-router-dom';

class Nav extends Component {
  render() {
    return (
        <ul style={{display:this.props.isLogin?"block":"none"}}>
            <li style={{display:this.props.isLogin?"none":"block"}}>
                <Link to="/">登陸</Link>
            </li>
            <li>
                <Link to="/Home">主頁</Link>
            </li>
            <li>
                <Link to="/About">關於咱們</Link>
            </li>
            <li>
                <Link to="/News">消息</Link>
            </li>
        </ul>
    );
  }
}

export default Nav;

NavigationReactRedux.js

import { connect } from 'react-redux';

//=====引入組件=====
import Navigation from './Navigation.js'

//=====react-redux 封裝組件=====

// 哪些 Redux 全局的 state 是咱們組件想要經過 props 獲取的?
function mapStateToProps(state) {
    return {
        isLogin:state.isLogin
    }
}

// 哪些 action 建立函數是咱們想要經過 props 獲取的?
function mapDispatchToProps(dispatch) {
    return {};
}

//封裝傳遞state和dispatch
var NavigationReactRedux = connect(mapStateToProps,mapDispatchToProps)(Navigation);

export default NavigationReactRedux

App.js咱們使用封裝後導航:

import React from 'react';
import {Route,  Link} from 'react-router-dom';
import './App.css';
import LoginReactRedux from './components/login/LoginReactRedux.js';
import HomeReactRedux from './components/home/HomeReactRedux.js';
import About from './components/about/About.js';
import NewsReactRedux  from './components/news/NewsReactRedux.js';
import NavigationReactRedux from './components/navigation/NavigationReactRedux.js';

function App() {
  return (
    <div className="App">      
      <NavigationReactRedux />
      <div>
          <Route exact path="/" component={LoginReactRedux}/>
          <Route exact path="/Home" component={HomeReactRedux}/>
          <Route path="/About" component={About}/>
          <Route path="/News" component={NewsReactRedux}/>
      </div>
    </div>
  );
}

export default App;

測試一下

邏輯級別

若是用戶直接輸入地址怎麼辦?因此咱們在路由對應的頁面都要加入登陸狀態的判斷,而後處理是留在當前頁面仍是跳到登陸頁面:

咱們登陸後的頁面只有三個,咱們先對Home作一個處理,其餘的相似:

HomeReactRedux.js:

import { connect } from 'react-redux';

//=====引入組件=====
import Home from './Home.js'


//=====react-redux 封裝組件=====

// 哪些 Redux 全局的 state 是咱們組件想要經過 props 獲取的?
function mapStateToProps(state) {
    return {
        isLogin:state.isLogin
    }
}

// 哪些 action 建立函數是咱們想要經過 props 獲取的?
function mapDispatchToProps(dispatch) {
    return {
        OUTLOGIN:function(history){
            dispatch({type:"OUT_LOGIN"})
            history.push("/")
        }
    };
}

//封裝傳遞state和dispatch
var HomeReactRedux = connect(mapStateToProps,mapDispatchToProps)(Home);

export default HomeReactRedux

Home.js

import React, { Component } from 'react';
import {Redirect} from 'react-router-dom';
//=====組件=====

class Home extends Component {
    
    render() {
        
        if(this.props.isLogin==false){
            return <Redirect to="/" />
        }
        return (
            <div>
                <h3>主頁</h3>
                <div>
                    <button onClick={this.outLogin.bind(this)}>退出登陸</button>
                </div>
            </div>
        );
    }

    outLogin(){
        this.props.OUTLOGIN(this.props.history);
    }
        
    componentDidMount() {
          console.log("Home渲染完畢")
    }    
}

export default Home
View Code

其餘路由頁面同理!!!其實也就是News頁面,目前咱們其它的頁面並不須要驗證

NewsReactRedux.js

import { connect } from 'react-redux';

//=====引入組件=====
import News from './News.js'


//=====react-redux 封裝組件=====

// 哪些 Redux 全局的 state 是咱們組件想要經過 props 獲取的?
//負責輸入邏輯,即將state映射到 UI 組件的參數(props)
function mapStateToProps(state) {
    return {
        isLogin:state.isLogin,
        list: state.NewsRedux.list
    };
}

// 哪些 action 建立函數是咱們想要經過 props 獲取的?
//負責輸出邏輯,即將用戶對 UI 組件的操做映射成 Action。
function mapDispatchToProps(dispatch) {
    return {
        SORTREVERSE:function(){
            dispatch({type:"SORT_REVERSE"})
        }
    };
}

//封裝傳遞state和dispatch
//把redux的狀態數據和action所有發射給News組件
var NewsReactRedux = connect(mapStateToProps,mapDispatchToProps)(News);

export default NewsReactRedux
View Code

News.js

import React,{ Component } from 'react';
import {Redirect} from 'react-router-dom';

//=====組件=====
class News extends Component {    
    constructor(props) {
        super(props);        
    }
    
    render() {
        if(this.props.isLogin==false){
            return <Redirect to="/" />
        }
        return (
            <div>
                <h3>消息</h3>
                <ul>
                    {
                        this.props.list.map(function(item,i){
                            return <li key={item.id}>
                                <a>{item.title}</a>
                                <span>{item.con}</span>
                            </li>                        
                        })    
                    }
                </ul>
                <button onClick={this.SORTREVERSE.bind(this)}>倒敘顯示</button>
            </div>
        );
    }
    SORTREVERSE(){
        this.props.SORTREVERSE();
    }
    componentDidMount() {
          console.log("News渲染完畢")
    }
    
}


export default News
View Code

刷新問題

咱們本地存儲可使用cookie還可使用localstorage,咱們刷新應用就獲取localstorage對登陸狀態的設置,而後action便可!

不過無論是cookie仍是localstorage若是用戶瀏覽器的安全級別高就完蛋了,咱們存放在這個2個裏面哪個都會遇到這個問題。

咱們或許能夠這樣作,在刷新咱們就向後臺發送一個請求,這個請求會返回用戶是否在登陸中和返回用戶的一些信息,根據狀態咱們用手動方法跳轉連接。

404錯誤

先建立一個src/components/exception/NotFind.js文件app.js

import React, { Component } from 'react';
//=====組件=====

class NotFind extends Component {
    
    render() {
        
        return (
            <div>
                <h1>404錯誤</h1>
                <div>
                    找不到對應的網頁
                </div>
            </div>
        );
    }
}

export default NotFind

 

使用router爲咱們提供的 Switch 組件:

import React from 'react';
import {Route,  Link,Switch} from 'react-router-dom';
import './App.css';
import LoginReactRedux from './components/login/LoginReactRedux.js';
import HomeReactRedux from './components/home/HomeReactRedux.js';
import About from './components/about/About.js';
import NewsReactRedux  from './components/news/NewsReactRedux.js';
import NavigationReactRedux from './components/navigation/NavigationReactRedux.js';
import NotFind from './components/exception/NotFind.js';

function App() {
  return (
    <div className="App">      
      <NavigationReactRedux />
      <div>
        <Switch>
          <Route exact path="/" component={LoginReactRedux}/>
          <Route exact path="/Home" component={HomeReactRedux}/>
          <Route path="/About" component={About}/>
          <Route path="/News" component={NewsReactRedux}/>
          <Route component={NotFind}/>            
                </Switch> 
      </div>
    </div>
  );
}

export default App;

打包

咱們執行下面命令:

npm run build

 

HashRouter方式

在使用BrowserRouter發佈的時候會遇到刷新404錯誤問題。咱們須要作一些設置。因此爲了省事幹脆用HashRouter

index.js: 

import React from 'react';
import ReactDOM from 'react-dom';
// import {BrowserRouter as Router} from 'react-router-dom';
import {HashRouter as Router} from 'react-router-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';

//redux 
import { createStore } from 'redux';
//react-redux(關聯react和redux)
import { Provider } from 'react-redux';

//reducers 狀態樹state和邏輯操做
import rootRedux from './rootRedux.js'

//建立狀態樹和設置

//生成狀態樹對象
const store = createStore(rootRedux);
ReactDOM.render(
    <Provider store={store}>
        <Router>
            <App />
        </Router>    
    </Provider>
, document.getElementById('root'));

// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister();

 

代碼下載

相關文章
相關標籤/搜索