react,react-router,redux+react-redux 构建一个React Demo
创建初始化应用
加速我们的npm。
npm install -g cnpm --registry=https://registry.npm.taobao.org
利用create-react-app 创建项目,执行下面创建我们的demo-react应用:
cd ......到你自己的路径
create-react-app reactdemo
我习惯使用VSCode,我用vscode打开这个文件夹,目录结构如下。

安装需要的依赖
我们的项目需要:
react-router:
react-router-dom
redux
react-redux
在VSCode中打开powershell(Ctrl+`)
依次安装
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();
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();
我们为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
利用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;
可以运行看看了。
登录处理
“/“地址就是我们的登录页面,我们点击登录跳转就可以了,不过我们会把用户的登录状态存放在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
其他路由页面同理!!!其实也就是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
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
刷新问题
我们本地存储可以使用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();

浙公网安备 33010602011771号