Android第③方登录与服务器端验证进程,react项目落成登录注册

By admin in 4858美高梅 on 2019年3月25日

会员登录在大家的大队人马门类中都有使用,比如在后台管理种类,它的首先步就须求您进行登录,还有在大家周边的京东、Tmall、微博云音乐等一多元的软件方面都供给实行登录。

react项目中贯彻登录注册简单残暴感悟

  在此之前只行使django和jquery做小项目支付,所以决定搞一搞react框架。React
特点:注脚式设计、虚拟DOM、JSX、组件、数据驱动。

开篇

随着一代的上进,未来对App必要不像早几年那样,大多数利用还是未联网的,现在的应用程序借使不可能与用户举办较好的相互,就会少了太多的竞争力。说到互相,就必需必要我们访问互联网,进行多少的互动。因为急需用户的信息,又不可或缺注册和登录的功效,近来天司空见惯的账号,使得部分用户不想再又去注册3个,那里对于开发人士也许中小集团来说最佳的选取便是直接使用第一方账号了,这里即使标题是android,但事实上那么些进程和什么平台是没有何样关联的。

上边大家直接上代码

全局安装react官方推荐脚手架 create-react-app

① 、环境搭建

其三方账号与后台的绑定

此间首要讲讲最广泛的QQ登录,首先大家须要的是去腾讯的开放平台去申请,以及读取官方文档,那些事物就不另行了。那里大家的重点是哪些将用户
音讯与后台举行相应的绑定,从前本人没本人写过后台,刚早先面对这么些时也是很模糊的,去百度搜出来的第叁方登录,基本上都是讲OAuth协议的,也多亏因为那点,越发不显著了。其实大家在移动端的登录与web端仍旧有较大的区分的,很关键的,就是登录后的回调。
既然如此要与后台实行绑定,肯定是索要唯一的标识符作为正式,因为保管安全性,所以大家在运动端举行登录后,并不能直接获取QQ账号,于是腾讯给大家提供了三拾三位的OPEN_ID用来作为唯一标识,并且区别的接纳OPEN_ID是见仁见智的,但对于3个利用而言,那是绝无仅有的标识符。
所未来台进行挂号时
1.亟待在服务器端判断数据库中中是还是不是留存那么些OPEN_ID
假使存在,就印证用户已经存在了,无需再开始展览登记。
2.只要不设有,
就从客户端向劳动器端传递Access_Token和OPEN_ID,服务器端接收后就在这一个地方举行认证

https://graph.qq.com/user/get_user_info?access_token=客户端传来的access_token&oauth_consumer_key=你申请到的appkey&openid=客户端传来的OPEN_ID&

呼吁后,那里会有一个赶回一个键ret,值为0代表回去用户消息成功,这里展开下判断就能够了。
Android第③方登录与服务器端验证进程,react项目落成登录注册。3.那样经过那两步就能与客户端登录的账号举行绑定。这里注意,登录其实就只是做贰个评释而已。

 1 fetch(url,{
 2     method: 'post', //使用post方法
 3     mode: 'cors', //跨域
 4     headers:{
 5         'Content-Type': 'application/x-www-form-urlencoded'
 6     },  //请求头
 7     body:'cellphone='+this.state.username+'&password='+this.state.password //方式数据
 8 }).then(res=>res.json())
 9     .then(json=>{if(json.code === 200){
10         localStorage['uid']=json.data.uid; //本地存储
11         this.props.history.replace("/detail")
12     }else {
13         console.log(json.data)
14     }})
npm install create-react-app -g

  1.安装npm、cnpm

# 安装node.js 从而安装npm,它会在当前用户家目录下生成node_moudules文件夹,来对npm安装的全局第三方包进行管理
brew install node

# npm安装cnpm
npm install -g cnpm --registry=https://registry.npm.taobao.org
npm config set registry https://registry.npm.taobao.org

# 测试
cnpm install express -g

使用JWT情势展开表达

上边说了与客户端与劳动器端之间的绑定(其实正是类似于注册进程,不过用户不知底而已,进度都以交由后台去做到)。
注册成功后,能够行使一种相比安全,并且相当的慢的求证办法,也正是JSON WEB
TOKEN形式开展认证,对前者精通的心上人应该很纯熟那种艺术,面生的能够去网上搜一下,用那种方式开始展览呼吁验证,无需教导敏感新闻。
1.第贰用账号密码实行呼吁,服务器端重返给大家2个加密后的值。
(那里若是大家用Open_Id作为用户的唯一标识符的话,能够把用户新闻的任何字段当作参数字传送递,那里就要整合实际的语言去举行修改了)
2.获取这些值后,能够保留在当地,等某个请求必要进行身份验证时,在把该值到场到请求头中。
3.呼吁时,还索要再加密后的那段值前边加上自定义的字符串。
那样一来固然服务器端重临的那么些值被走漏,用户的敏感音讯仍是能够获得平安确定保障。在活动端进行呼吁时,直接带走请求头,就能一点也不慢的完毕身份验证,达成单点登录。

前天大家得以见到,作者利用的post方法向劳动器端发送数据,当呼吁成功的时候,大家一时把它存款和储蓄在该地,那里也得以构成redux来做,有趣味的能够参考作者的另一篇博客初步精通redux来成功,然后开始展览跳转,就算请求未遂大家能够直接重回它失利的由来,

创建react项目

  2.安装react全家桶

# 安装create-react-app
cnpm install create-react-app -g
# 利用create-react-app 来创建一个项目
create-react-app 项目名称     # 假设这里的项目名称是my-app
# 进入项目文件夹
cd my-app
# 生成配置文件
# cnpm run eject       # 生成scripts文件夹和config文件夹,cnpm
# 启动服务
cnpm start           # 它相当于执行 scrpits/starts.js,这个已经在package.json中配置好了命

结尾

因为此地那是演讲那几个进度,没有切实可行的代码,所以就一时半刻到那里了。因为自个儿以为,对一门语言基本的事物明白了,关键就是搞精晓那几个事情逻辑,理解交互的进度,不局限于某一种语言,结合实际的框架去做相应的调整。经验无非也是进一步明亮整个进程,才能得来的。

接下去说一下第1,验证它是还是不是登录,大家特意建三个js文件

create-react-app react-login-register

  3.文件目录结构

my-app
    - config                        # 项目默认配置文件,由npm run eject生成
    - .gitignore                    # git配置
    - node_modules                  # 本地第三方安装包
    - package.json                  # npm项目配置,里面记录了项目的名字、版本、依赖包、和自定义配置 
    - package-lock.json
    -  public                       # 公共资源
       - index.html             # 静态页面
    - README.md            
    -  scripts                      # 配置pacakage.json中的"scripts"定义的命令
        - build.js                  # build命令,可由npm build执行,生成静态文件用于迁移到生产环境
        - start.js                  # start命令
        - test.js                
    -  src
        - App.css                   # 创建项目时自动生成的css样式,没用
        - App.js                    # react组件
        - App.test.js
        - index.css                 # 入口文件的样式
        - index.js                  # 入口文件
        - log.svg
        - registerServiceWorker.js

  项目运转时,会加载public下的index.html文件,并跟着实施index.js,从而做到整个页面包车型大巴渲染。

 1 import React from 'react';
 2 import {Route,Redirect} from 'react-router-dom'
 3 const AuthRoute = ({ component: Component, ...rest }) => (
 4     <Route {...rest} render={props => (
 5         localStorage.getItem("uid") ? (//如果本地存储里面有我们就进行跳转,没有就进行重定向返回登录页面
 6             <Component {...props}/>
 7         ) : (
 8             <Redirect to={{
 9                 pathname: '/news', //重定向的页面
10                 state: { from: props.location }
11             }}/>
12         )
13     )}/>
14 );
15 export {
16     AuthRoute
17 }

跻身项目并运转

二 、react一些概念

<!--public/index.html简化如下-->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <meta name="theme-color" content="#000000">
    <title>React App</title>
    <style>
        body {
            margin: 0;
            padding: 0;
        }
    </style>
</head>
<body>
    <div id="root"></div>
</body>
</html>

  react简单示例只保留简化后的public/index.html和src/index.js。

末段大家到主路由页面引入大家的这些注解登录

npm start

  1.ReactDOM.render

  index.js中的ReactDOM.render函数用于将标签也许零部件渲染到public下的页面中。第三个参数能够是不管三七二十一的单个标签、div包裹的三个标签,以及自定义的机件Component。

1 import {AuthRoute} from '../assets/common/function'

4858美高梅 1

  2.React.Component

  React.Component是自定义组件的父类,必须重写render方法来实现组件的扬言和重临。所谓组件,正是用js类定义的一组html标签、样式、数据、事件等的结合,该类能够用<类名
/>的措施开始展览标签化(实例化),实例化时自动调用render方法,并最终交由React.render实现渲染。

把Route改成AuthRoute

image.png

  3.零件内部参数注脚和应用

  组件内部可以申明参数,无论是textNode如故其余Node,都是以 { 参数 }
的花样被组件调用。组件react虚拟DOM的有血有肉落到实处方式,它来形成对页面和事情逻辑的细分。

// src/index.js
import React from 'react';
import ReactDOM from 'react-dom';

class App extends React.Component{
    render (){
        const string = "hello, react!";
        const style1 = {
            color: "white"
        };
        const style2 = {
            textAlign:"center",
            fontStyle: "border",
            backgroundColor: "green"
        };
        return (
            <div style={style1}>
                {/*这是一段注释*/}
                <h2 style={style2}>{ string }</h2>
            </div>
        )
    }
}

ReactDOM.render(<div>
    <h2>hello, react!</h2>
    <App />
</div>, document.getElementById('root'));

  组件间能够完结嵌套。

import React from 'react';
import ReactDOM from 'react-dom';

class App extends React.Component{
    render (){
        return <h2>这是子组件</h2>
    }
}
class App2 extends React.Component{
    render (){
        const style = {
            backgroundColor: "blue"
        };
        return (
            <div style={style}>
                <h2>这是父组件</h2>
                <App />
            </div>
        )
    }
}

ReactDOM.render(<App2 />, document.getElementById('root'));

当今大家的会员登录就做到了。

安装此demo中各类重视

  4.静态数目传递–props

  组件间数据通过this.props属性来开始展览多少传递。父组件在标签化时经过质量的点子传递数据,子组件通过this.props来博取具有的数据。

import React from 'react';
import ReactDOM from 'react-dom';

class App extends React.Component{
    render (){
        return <h2>App: { this.props.name }, { this.props.age }</h2>
    }
}
class App2 extends React.Component{
    render (){
        const name = "Sun", age=5000;

        return (
            <div>
                <h2>App2: { this.props.name }, { this.props.age }</h2>
                <App name={name} age={age} />
            </div>
        )
    }
}
ReactDOM.render(<App2 name="Li" age="20" />, document.getElementById('root'));

  注意:1.props不得不传递静态数据,无法与用户交互;2.{}不是Object对象,假诺想写<App2
obj={name: “Li”, age: 20}
/>,就无法不超前申明。3.style样式作为数据传递是不行且荒谬的。

const obj = {name: "Li",age: 20};
<App2 obj={obj}/>   
  npm install antd-mobile -S  // UI框架
  npm install redux -S  // redux
  npm install react-redux -S  // react-redux
  npm install react-router-dom -S  // react路由
  npm install axios -S // react请求交互
  npm install redux-thunk -S // redux中间件 配合redux解决异步问题
  npm install babel-plugin-transform-decorators-legacy -D // 配合react-redux 支持@装饰器
  npm install mongoose -D // 安装mondoose MongoDB数据库插件
  npm install express -S // 基于node开发,搭建请求环境
  npm install babel-plugin-import -S antd-mobile按需加载babel插件
  npm install body-parser -S  后台数据接口用户传进来的参数解析插件
  npm install cookie-parser -S 通过cookie存储用户登录状态
  PS: 合并安装飞起来~

  npm run eject // 释放react关于webpack的封装配置
  sudo brew install mongodb(我这里是mac安装方式,其他平台可百度下)

  5.动态数据交互–state

  state用于承担处理动态数据。数据需求在构造函数中经过this.state事先注解,并在事变中通过this.setSate实现多少更新。

import React from 'react';
import ReactDOM from 'react-dom';

class App extends React.Component{
    constructor(props){
        super(props);
        this.state = {
            nameList:["Sun", "Li", "Zhao", "Qian"]
        }
    }
    addPerson(){
        this.setState({
            nameList: [...this.state.nameList, "zhou"]
        })
    }
    render (){
        return (
            <div>
                <button onClick={()=>this.addPerson()}>加入周先生</button>
                <ul>
                    {this.state.nameList.map((name, index) => <li key={index+1}>{name}</li>)}
                </ul>
            </div>

        )
    }
}
ReactDOM.render(<App />, document.getElementById('root'));

删掉 src目录上面全体文件并新建index.js

  6.轩然大波绑定

  在react中,事件以属性的艺术平昔在标签中运用,其调用函数要用一层函数包裹。调用函数能够一贯写在目的内部,并且要用bind实行监听和创新。

  在上边的言传身教中,大家透过这一行代码代替了bind的长河:

<button onClick={()=>this.addPerson()}>加入周先生</button>

  鉴于js中从未局地成效域唯有函数功能域,所以如若一向写onClick={this.addPerson}时,this指的正是windo对象。用一层函数包裹时,this对象指的正是那几个函数。

  其余的做法有二种:

  首先,在事件绑定时直接使用this.function:

<button onClick={this.addPerson}>加入周先生</button>

  其次能够用箭头函数来声称addPerson函数,本质上和上边使用的方法一致:

addPerson = ()=>{
    this.setState({
        nameList: [...this.state.nameList, "zhou"]
    })
};

  或许能够不改addPersonn函数,而是在构造器中添加上如此一句话:

this.addPerson = this.addPerson.bind(this)
 // index.js
import React from 'react'
import ReactDOM from 'react-dom'

ReactDOM.render(
    <div>Demo</div>,
    document.getElementById('root')
)

  7.尺度渲染

  render自己是八个实例方法,援救js中的各个代码逻辑。能够用if-else来支配再次回到结果。条件渲染在用户登录和重定向中应用的可比频繁。

// src/index.js
import React from 'react';
import ReactDOM from 'react-dom';

class App extends React.Component{
    constructor(props){
        super(props);
        this.state = {isLogIn: false}

    }
    login (){
        this.setState({isLogIn: true})
    };
    logout(){
        this.setState({isLogIn: false})
    }
    render (){
        let button=null;
        if (this.state.isLogIn){
            button = <button onClick={()=>this.logout()}>注销</button>
        }else {
            button = <button onClick={()=>this.login()}>登录</button>
        }
        return button
    }
}
ReactDOM.render(<App />, document.getElementById('root'));

  也足以用安慕希表达式简写:

render (){
    return (
        <div>
            {this.state.isLogIn ? <button onClick={()=>this.logout()}>注销</button> : <button onClick={()=>this.login()}>登录</button>}
        </div>
    )
}

  注意:由于{}传递的数据足以是任意值,所以必要用div包裹。

4858美高梅 2

  八 、组件的生命周期

  组件的宣示周期可分为三个情形:Mounting,已插入真实
DOM;Updating,正在被重复渲染;Unmounting:已移出真实
DOM。如下图所示(本图根据罗丝n先生示例改写)。

  4858美高梅 3

  各种函数的释义(摘自菜鸟教程):

  componentWillMount 在渲染前调用,在客户端也在服务端。

  componentDidMount :
在第②遍渲染后调用,只在客户端。之后组件已经变更了对应的DOM结构,能够经过this.getDOMNode()来展开访问。
如若您想和任何JavaScript框架一起行使,能够在那一个方法中调用setTimeout,
setInterval或许发送AJAX请求等操作(幸免异部操作阻塞UI)。

  componentWillReceiveProps 在组件接收到三个新的 prop
(更新后)时被调用。这几个情势在伊始化render时不会被调用。

  shouldComponentUpdate
重回八个布尔值。在组件接收到新的props或然state时被调用。在开头化时也许利用forceUpdate时不被调用。
能够在您确认不须求立异组件时利用。

shouldComponentUpdate(nextProps, nextState) {
    console.log(nextProps, nextState);
    console.log(this.props, this.state);
    if(this.state.update === true){
        // 只有在给定条件发生的时候返回true,也就是可以重新渲染
        return true
    }
   // 只有在给定条件发生的时候返回true,也就是可以重新渲染
   if(nextState.update === true){
        // 在下一个状态达到条件时重新渲染
        return true
    }
    return false // 其它条件返回false,不重新渲染
}

  componentWillUpdate在组件接收到新的props或许state但还从未render时被调用。在初阶化时不会被调用。

  componentDidUpdate
在组件达成更新后立马调用。在发轫化时不会被调用。

  componentWillUnmount在组件从 DOM 中移除的时候马上被调用。

image.png

  玖 、综合:使用component请求后台数据并交由组件

  在my-app下创制server/server.js文件,运维三个后端服务:

const express = require('express');

const app = express();
app.get("/data", function (req, res) {
    res.json({"name": "old monkey", "age": 5000})
});
app.listen(3002, function () {
    console.log("Node app start at port 3002.")
});

  Terminal运营该服务: node
server/server.js。此时得以访问 

  安装axios: cnpm install axios
–save。并在package.json的安顿文件中加上”proxy”配置,让它转换端口到3002:

// package.json
{
  "name": "my-app",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "axios": "^0.18.0",
    "react": "^16.4.1",
    "react-dom": "^16.4.1",
    "react-scripts": "1.1.4"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test --env=jsdom",
    "eject": "react-scripts eject"
  },
  "proxy": "http://localhost:3002"
}

  在src/index.js中写组件:

// src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import axios from 'axios';

class App extends React.Component{
    constructor(props){
        super(props);
        this.state = {name: "monkey", age: 100}

    }
  // 在component内部使用ajax请求数据,并通过setState传递给App。
    componentDidMount(){
        axios.get("/data").then(res=>{
            if(res.status === 200){
                console.log(res);
                this.setState({name: res.data.name, age: res.data.age});
            }
        }, err=>{
            console.log(err);
        })
    }
    addAge(){
        this.setState({age: this.state.age + 1})
    };
    decAge(){
        this.setState({age: this.state.age - 1})
    }
    render (){
        const style={
            display: "inline-block",
            width: "150px",
            height: "40px",
            backgroundColor: "rgb(173, 173, 173)",
            color: "white",
            marginRight: "20px"
        };
        return (
            <div>
                <h2>this {this.state.name } is { this.state.age } years old.</h2>
                <button style={style} onClick={()=>this.addAge()}>增加一岁</button>
                <button style={style} onClick={()=>this.decAge()}>减少一岁</button>
            </div>
        )
    }
}
ReactDOM.render(<App />, document.getElementById('root'));

在src目录新建一下文书和文书夹

三、redux、redux-thunk与react-redux

  react本身能够做到动态数据的监听和创新,固然不是必备能够不适用redux。

  安装redux: cnpm install redux
–save。

4858美高梅 4

  1.react中坚用法

  redux是单身的用于状态管理的第③方包,它确立景况机来对单项数据开始展览管理。

4858美高梅 5

  上图是私有粗浅的接头。用代码验证一下:

// src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { createStore } from "redux";

function reducer(state={name: "monkey", age: 5000}, action){
    switch (action.type){
        case "add":
            state.age ++;
            return state;
        case "dec":
            if (state.age <= 4995){
                state.name = "small monkey";
            }
            state.age --;
            return state;
        default:
            return state;
    }
}
const store = createStore(reducer);
const add = {type: "add"}, dec={type: "dec"};


class App extends React.Component{
    render (){
        const style={
            display: "inline-block",
            width: "150px",
            height: "40px",
            backgroundColor: "rgb(173, 173, 173)",
            color: "white",
            marginRight: "20px"
        };
        const store = this.props.store;
        // console.log(store.getState());
        const obj = store.getState();
        // console.log(obj);
        return (
            <div>
                <h2>this { obj.name } is { obj.age } years old.</h2>
                <button style={style} onClick={()=>store.dispatch(this.props.add)}>增加一岁</button>
                <button style={style} onClick={()=>store.dispatch(this.props.dec)}>减少一岁</button>
            </div>
        )
    }
}
function render() {
    ReactDOM.render(<App store={store} add={ add } dec={ dec } />, document.getElementById('root'));
}
render();
store.subscribe(render);

  因为action必须是个对象,所以只可以写成add = {type:
“add”}的花样,而不可能直接写参数”add”。同样地,在reducer中写switch时将action.type作为参数。

  action和state一一对应,要利用action必须要在reducer里注解。

  redux没有用state来兑现动态数据更新,而是经过props来传递数据,由此在组件内部只好通过props获取store,以及store.getState()获取state。

  redux将ReactDOM.render进行了三回封装来安装监听。

  redux对数码和零部件进行精晓耦,由此能够拓展文件拆分。

  把action写成函数的花样:

// src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { createStore } from "redux";

const Add = "add", Dec="dec";
function reducer(state={name: "monkey", age: 5000}, action){
    switch (action.type){
        case Add:
            state.age ++;
            if (state.age > 5005){
                state.name = "old monkey";
            }
            return state;
        case Dec:
            if (state.age <= 4995){
                state.name = "small monkey";
            }
            state.age --;
            return state;
        default:
            return state;
    }
}
const store = createStore(reducer);
class App extends React.Component{
    render (){
        const style={
            display: "inline-block",
            width: "150px",
            height: "40px",
            backgroundColor: "rgb(173, 173, 173)",
            color: "white",
            marginRight: "20px"
        };
        const store = this.props.store;
        const state = store.getState();
        return (
            <div>
                <h2>this { state.name } is { state.age } years old.</h2>
                <button style={style} onClick={()=>store.dispatch(add())}>增加一岁</button>
                <button style={style} onClick={()=>store.dispatch(dec())}>减少一岁</button>
            </div>
        )
    }
}
function render() {
    ReactDOM.render(<App store={store} add={ add } dec={ dec } />, document.getElementById('root'));
}
render();
store.subscribe(render);

image.png

   2.react异步

  当二个父组件中有三个子组件时,假设每二个零件产生情形变化,store都要双重渲染3回。使用异步能够只重复渲染发生事态变化的零件。

  安装redux和thunk的中游件redux-thunk:cnpm install
redux-thunk。引入八个函数,并修改createStore如下,其余代码保持不变:

import { createStore, applyMiddleware } from "redux";
import thunk from 'redux-thunk';

const store = createStore(reducer, applyMiddleware(thunk));

  当然还足以利用compose在控制台添加调节和测试功效,前提是那亟需在浏览器中安装redux插件。

import { createStore, applyMiddleware, compose } from "redux";
const store = createStore(
    reducer,
    compose(
        applyMiddleware(thunk),
        window.devToolsExtension?window.devToolsExtension():f=>f
    )
);

components 放置组件
containers 放置页面
redux 放置redux成分集合文件(定义各类常量,reducers, create actions)
reducer.js 集合redux里面各类文件

  3.组件间与状态机、状态机与状态机

  内层的零件调用dispatch,那么它的父组件向来到最外层组件都急需使用store属性一层一层地传递状态机。

  状态机与状态机之间必要采用redux中的combineReducers合并成三个指标。并由dispatch调用的action来寻找其相应的reducer一流重临的state。

  举个例证:

  在src目录下新建index.redux.js文件,建立多少个reducer以及个其他action。

// src/index.redux.js
const Add = "add", Dec="dec";
export function reducerOne(state={name: "monkey", age: 5000}, action){
    switch (action.type){
        case Add:
            state.age ++;
            if (state.age > 5005){
                state.name = "old monkey";
            }
            return state;
        case Dec:
            if (state.age <= 4995){
                state.name = "small monkey";
            }
            state.age --;
            return state;
        default:
            return state;
    }
}
export function add() {
    return {type: Add}
}
export function dec() {
    return {type: Dec}
}

export function reducerTwo(state="孙悟空", action){
    if(action.type >= 5005){
        state = "斗战胜佛";
    }else if(action.type <= 4995){
        state = "小猕猴";
    }
    return state;
}
export function monkey(number) {
    return {type: number}
}

4858美高梅 ,  在src/index.js中写入如下代码:

// src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { createStore, applyMiddleware, combineReducers } from "redux";
import thunk from 'redux-thunk';

import { reducerOne, reducerTwo, add, dec, monkey } from "./index.redux";

const reducer = combineReducers({reducerOne, reducerTwo});
const store = createStore(reducer, applyMiddleware(thunk));

class Nick extends React.Component{
    render(){
        const store = this.props.store;
        const state = store.getState();
        return <h2 >Now this monkey is so-called { state.reducerTwo }</h2>
    }
}
class Monkey extends React.Component{
    render (){
        const style={
            display: "inline-block",
            width: "150px",
            height: "40px",
            backgroundColor: "rgb(173, 173, 173)",
            color: "white",
            marginRight: "20px"
        };
        const store = this.props.store;
        const state = store.getState();
        const name = state.reducerOne.name, age = state.reducerOne.age;
        return (
            <div>
                <h2>this { name } is { age } years old.</h2>
                <button style={style} onClick={()=>{store.dispatch(add()); store.dispatch(monkey(age))}}>增加一岁</button>
                <button style={style} onClick={()=>{store.dispatch(dec()); store.dispatch(monkey(age))}}>减少一岁</button>
                <Nick store={store} />
            </div>
        )
    }
}
function render() {
    ReactDOM.render(<Monkey store={store} add={ add } dec={ dec } />, document.getElementById('root'));
}
render();
store.subscribe(render);

  在Monkey组件中,onClick同时触发七个状态机reducerOne,reducerTwo。Nick组件接收点击事件触发的事态修改以往的store,并从中获得相应的字段举行渲染。在那里必要注意:

  1.施用combineReducers将Monkey复合组件涉及到的状态机进行联合。

  2.多少个reducer时,store.getState()获取的是一个目的。能够通过reducer名来获取对应reducer的state。

  3.store和action必须顶级一级的往下传递。

4858美高梅 6

  4.使用react-redux

   react-redux简化了redux在组件的各种传递难题。那里对下面的代码实行改动:

  -
src/index.redux.js文件内容如下:reducer中的state此时必须另行定义,无法向来再回到原来的state。然后微调了一晃显得的多寡。

// src/index.redux.js
const Add = "add", Dec="dec";
export function reducerOne(state={name: "monkey", age: 5000}, action){
    switch (action.type){
        case Add:
            if (state.age >= 5004){
                state.name = "old monkey";
            }else if(state.age > 4994 && state.age < 5004){
                state.name = "monkey";
            }
            return {...state, age: state.age+1};
        case Dec:
            if (state.age <= 4996){
                state.name = "small monkey";
            }else if(state.age > 4996 && state.age <= 5005){
                state.name = "monkey";
            }
            return {...state, age: state.age-1};
        default:
            return state;
    }
}
export function add() {
    return {type: Add}
}
export function dec() {
    return {type: Dec}
}
export function reducerTwo(state="孙悟空", action){
    if(action.type >= 5004){
        return "斗战胜佛";
    }else if(action.type <= 4996){
        return "小猕猴";
    }
    return state;
}
export function monkey(number) {
    return {type: number}
}

  - src/index.js内容如下:

import React from 'react';
import ReactDOM from 'react-dom';
import { createStore, applyMiddleware, combineReducers } from "redux";
import thunk from 'redux-thunk';
import { Provider, connect } from 'react-redux';

import { reducerOne, reducerTwo, add, dec, monkey } from "./index.redux";

const reducer = combineReducers({reducerOne, reducerTwo});
const store = createStore(reducer, applyMiddleware(thunk));

class Nick extends React.Component{
    render(){
        return <h2 >Now this monkey is so-called { this.props.nick }</h2>
    }
}
class Monkey extends React.Component{
    render (){
        const style={
            display: "inline-block",
            width: "150px",
            height: "40px",
            backgroundColor: "rgb(173, 173, 173)",
            color: "white",
            marginRight: "20px"
        };
        return (
            <div>
                <h2>this { this.props.reducerOne.name } is { this.props.reducerOne.age } years old.</h2>
                <button style={style} onClick={()=>{this.props.add(); this.props.monkey(this.props.reducerOne.age)}}>增加一岁</button>
                <button style={style} onClick={()=>{this.props.dec();this.props.monkey(this.props.reducerOne.age)}}>减少一岁</button>
                <Nick nick={ this.props.reducerTwo } />
            </div>
        )
    }
}
const mapStateProps = state=>{
    return { reducerOne: state.reducerOne, reducerTwo: state.reducerTwo }
};
const actionCreator = { add, dec, monkey };
Monkey = connect(mapStateProps, actionCreator)(Monkey);  // 其实是加了一层装饰器


function render() {
    ReactDOM.render(
        <Provider store={store}>
            <Monkey/>
        </Provider>
        , document.getElementById('root'));
}
render();

  首先,从react-redux中引入Provider,用来包装app
Monkey,然后将store传递给Provider即可。

  其次,用装饰器对app
Monkey进行李装运裱,并传递进入八个参数,第叁个参数是组件所急需的reducer中的状态(那里把四个reducer的景色全部写了进去),第二个参数是action指令。connect函数会依照将reducer和action一一对应起来,所以后边一贯用action函数即可。

  再度,onClick绑定action只须要写成onClick={this.props.action}即可,这里须求变更三个状态,所以用箭头函数包裹了一下。

  connect的那种写法看起来不爽,能够用装饰器的款式写。不过急需做一下babel配置:

# 1.生成配置文件
cnpm run eject
# 2.安装插件
cnpm install babel-plugin-transform-decorators-legacy --save
# 3.在package.json中的"babel"中做如下配置:
src/package.json

"babel": {
  "presets": [
    "react-app"
  ],
  "plugins":[
    "transform-decorators-legacy"
  ]
},

  此时将装饰器的这三行代码改为一行,写在Monkey类下边即可。

@connect(state=>({ reducerOne: state.reducerOne, reducerTwo: state.reducerTwo }), { add, dec, monkey })
class Monkey extends React.Component{
  ...
}

image.png

四、react-router-dom

  react-router-dom是特意用于拍卖路由的零部件。

  BrowserRouter用以包裹app,它会在this.props添加一些环境参数,那尤其用。

  Route用于将路由和组件绑定,贰个路由对应1个页面(组件),大概Link。其首要的二日性格为path和component,path能够选用目的,也能够吸收接纳三个url字符串。

  Link用于将链接和Route进行绑定。

  Switch用于当前路由下的具有子路由,一般和Redirect合营使用,用于重定向那1个未定义的子路由。

  Redirect:重定向。其重点的习性为to,只好指向二个url字符串。

  用例一: Route + Componnet + Redirect

  1.建立多个路由指向多个零部件:Login和Home,
以及3个用户登录意况的reducer。

  2.进入时首先重定向到Home页面.Home组件判断用户登录状态,登录则打字与印刷登录新闻并提供注销按钮,点击按钮会重定向到Login页面;未登录则平素重定向到Login页面。

  3.用户登录则重定向到Home页面,未登录则打字与印刷登录提醒并提供登录按钮,点击按钮会重定向到Home页面。

// src/index.js
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, Switch, Redirect } from 'react-router-dom';

import { reducer } from "./reducer";
import { Login } from "./Login";
import { Home } from "./Home";

const store = createStore(reducer, applyMiddleware(thunk));
ReactDOM.render(
    (<Provider store={store}>
        <BrowserRouter>
           <Switch>
               <Route path="/login" component={ Login } />
               <Route path="/monkey" component={ Home } />
               <Redirect to={{ pathname: "/monkey" }}/>
           </Switch>
        </BrowserRouter>

    </Provider>),
    document.getElementById('root')
);

// src/Home.js
import React from "react";
import {connect} from 'react-redux';
import { Redirect } from 'react-router-dom';
import {logout} from "./reducer";

@connect(state=>state, {logout})
class Home extends React.Component{
    render(){
        const app = (
            <div>
                <h2>这是home界面</h2>
                <button onClick={this.props.logout}>点击注销</button>
            </div>
        );
        return this.props.isLogin ? app : <Redirect to={{ pathname: "/login"}} />
    }
}
export { Home }

// src/Login.js
import React from 'react';
import {connect} from 'react-redux';
import {Redirect} from 'react-router-dom';
import {login} from "./reducer";

@connect(state=>state, {login})
class Login extends React.Component{
    render(){
        console.log(this.props);
        const app = (
            <div>
                <h2>请先登录!</h2>
                <button onClick={this.props.login}>点击登录</button>
            </div>
        );
        return this.props.isLogin ? <Redirect to={{pathname: "/home"}} /> : app
    }
}
export { Login }

// src/reducer.js
// 用户登录状态
const LOGIN = "login";
const LOGOUT = "logout";

export function reducer(state={isLogin: false, user: "Li"}, action) {
    switch (action.type){
        case LOGIN:
            return {...state, isLogin:true};
        case LOGOUT:
            return {...state, isLogin:false};
        default:
            return state
    }
}

export function login() {
    return {type: LOGIN}
}
export function logout() {
    return {type: LOGOUT}
}

  用例二:Route + Component + Link

// src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import {BrowserRouter, Route, Switch, Redirect, Link } from 'react-router-dom';


function LinkOne(){
    return <h2>我是LineOne组件,被Route绑定给了一个Link链接</h2>
}
function LinkTwo() {
    return <h2>我是LineThree组件,被Route绑定给了一个Link链接</h2>
}
function LinkThree(){
    return <h2>我是LineThree组件,被Route绑定给了一个Link链接</h2>
}
function Test(){
    return <h2>这是无效的路由,你不要搞事情!</h2>
}

ReactDOM.render(
    (<BrowserRouter>
        <div>
            <ul>
                <li><Link to="/">LinkOne</Link></li>
                <li><Link to="/two">LinkTwo</Link></li>
                <li><Link to="/three">LinkThree</Link></li>
            </ul>
            <Switch>
                <Route path="/" exact component={ LinkOne } />
                <Route path="/two" component={ LinkTwo } />
                <Route path="/three" component={ LinkThree } />
                <Route path='/:location' component={ Test } />
            </Switch>
        </div>
    </BrowserRouter>),
    document.getElementById('root')
);

 

  

 

containers 新建登录注册五个页面基本内容

import React, {Component} from 'react'

class Login extends Component {
    render() {
        return (
            <div>登录页面</div>
        )
    }
}

export default Login

redux 新建user.redux.js

export function user(state=0,action) {
    switch (action.type) {
        default:
            return state;
    }
}

reducer.js 引入user.redux.js 并合并

import {combineReducers} from 'redux'

import {user} from './redux/user.redux'

export default combineReducers({user})

components 新建 checkLogin 判断用户是不是登录

import {Component} from 'react';
import {withRouter} from 'react-router-dom'

@withRouter
class CheckLogin extends Component {
    componentDidMount() {
         // 在这里请求相关接口判断用户是否完成登录
        axios.get('xxxxx')
            .then(res => {
                if(res.status === 200) {
                    if(res.data.code === 0) {

                    }else {
                        this.props.history.push('/login')
                    }
                }
            })
    }
    render() {
        return null;
    }
}

export default CheckLogin;

index.js实现报到注册准备工作

import React from 'react'
import ReactDOM from 'react-dom'

// 配合applyMiddleware解决redux异步问题
import thunk from 'redux-thunk'

// createStore接受reducer生成stote compose合并生成store其他数据 applyMiddleware接受thunk解决redux异步问题
import {createStore, compose, applyMiddleware} from 'redux'

// Provider负责传递store
import {Provider} from 'react-redux'

// 引入react-router-dom各种路由元素
import {BrowserRouter as Router, Route} from 'react-router-dom'

// 引入判断是否登录组件
import CheckLogin from './components/checkLogin/CheckLogin'

// 引入页面路由组件
import Login from './containers/login/login'
import Register from './containers/register/register'

// 生成store
import reducer from './reducer'

const store = createStore(reducer, compose(
    applyMiddleware(thunk), //解决redux异步问题
    window.devToolsExtension ? window.devToolsExtension() : f => f // chrome控制台redux工具
))

// 页面渲染
ReactDOM.render(
    <Provider store={store}>
        <Router>
            <div className="react-login-register">
                    <CheckLogin></CheckLogin>
                    <Route path='/login' component={Login}></Route>
                    <Route path='/register' component={Register}></Route>
            </div>
        </Router>
    </Provider>,
    document.getElementById('root')
)

从控制台,能够看来redux相关新闻(user: 定义的reducer user
值是回到state的起始值0)
PS:前提是chrome安装redux插件Redux DevTools

4858美高梅 7

image.png

在package.json的babel里添加一下插件

 "plugins": [
      "transform-decorators-legacy", // 配合react-redux 的connent 支持装饰器写法
      [
          "import",
          {
            "libraryName": "antd-mobile",
            "style": "css"
          }
      ] // antd-mobile 按需加载
  ]

接下去完善登录注册页面包车型客车UI 和中坚交互逻辑

签到页面

   // login.js
  import React, {Component} from 'react'
  import {List,InputItem,WingBlank,WhiteSpace, Button} from 'antd-mobile'
  import Logo from '../../components/logo/logo'

  class Login extends Component {
      render() {
          return (
              <div className="page-login">
                  <Logo />
                  <WhiteSpace></WhiteSpace>
                  <WhiteSpace></WhiteSpace>
                  <WhiteSpace></WhiteSpace>
                  <WhiteSpace></WhiteSpace>
                  <List>
                      <InputItem>lbj-账号</InputItem>
                      <InputItem>lbj-密码</InputItem>
                  </List>
                  <WhiteSpace></WhiteSpace>
                  <WhiteSpace></WhiteSpace>
                  <WhiteSpace></WhiteSpace>
                  <WhiteSpace></WhiteSpace>
                  <WhiteSpace></WhiteSpace>
                  <WingBlank>
                      <Button type="primary">登录</Button>
                      <WhiteSpace></WhiteSpace>
                      <Button onClick={this.handleGoRegister.bind(this)} type="primary">去注册</Button>
                  </WingBlank>
              </div>
          )
      }
      /*
      *     去注册
      * */
      handleGoRegister() {
          this.props.history.push('/register');
      }
  }

  export default Login

挂号页面

  import React, {Component} from 'react'
  import {List, InputItem, WingBlank, WhiteSpace, Button, Radio} from 'antd-mobile'
  import Logo from '../../components/logo/logo'

  class Register extends Component {
      constructor(props) {
          super(props);
          this.state = {
              username: '', //账号
              pwd: '', // 密码
              pwdConfirm: '', // 确认密码
              type: 'worker', // 用户类型 默认求职者
          }
      }

      render() {
          const RadioItem = Radio.RadioItem
          return (
              <div className="page-register">
                  <Logo/>
                  <List>
                      <InputItem onChange={value => this.handleChange('username', value)}>lbj-账号</InputItem>
                      <InputItem onChange={value => this.handleChange('pwd', value)}>lbj-密码</InputItem>
                      <InputItem onChange={value => this.handleChange('pwdConfirm', value)}>lbj-确认</InputItem>
                  </List>
                  <WhiteSpace></WhiteSpace>
                  <List>
                      <RadioItem
                          onClick={() => this.handleChange('type', 'worker')}
                          checked={this.state.type === 'worker'}>牛人    </RadioItem>
                      <RadioItem
                          onClick={() => this.handleChange('type', 'boss')}
                          checked={this.state.type === 'boss'}>BOSS</RadioItem>
                  </List>
                  <WhiteSpace></WhiteSpace>
                  <WhiteSpace></WhiteSpace>
                  <WhiteSpace></WhiteSpace>
                  <WingBlank>
                      <Button type="primary">登录</Button>
                      <WhiteSpace></WhiteSpace>
                      <Button onClick={this.handleGoLogin.bind(this)} type="primary">已有账号,去登录</Button>
                  </WingBlank>
              </div>
          )
      }

      /*
      *     去登录
      * */
      handleGoLogin() {
          this.props.history.push('/login')
      }

      /*
      *     绑定表单值
      * */
      handleChange(key, val) {
          this.setState({
              [key]: val
          })
      }
  }

  export default Register

下一步,便是搭建数据库,此前设置的MongoDB,mongoose, express派上了用场

品类根目录上面新建server文件夹
文本夹上面新建server.js和user.js
server.js

const express = require('express');
const userRouter = require('./user')

// 新建app
const app = express();

app.use('/user',userRouter)

app.listen(8000, () => {
    `server is running at port 8000 success~~~`
})

user.js

const express = require('express')

// 生成express路由中间件
const Router = express.Router();

// CheckLogin.js 用户查询用户是否登录的接口
Router.get('/info', (req, res) => {
    return res.json({code: 1,msg: '未登录'})
})

module.exports = Router

进入server文件夹 运行

npm install nodemon -g   // nodemon 是基于node的实时监听的工具
nodemon server.js

4858美高梅 8

image.png

那会儿得以见到请求地址已经能够访问
这里抛出一个难题,
品种地址是http://localhost:3001/,
伸手地址是http://localhost:8000/user/info
出于浏览器同源策略限制,直接伸手会设有着跨域难点(协议,域名,端口任何二个不等同,即视为跨域),所以那边能够经过代理请求的章程化解跨域难点
在package.json 添加 “proxy”:
“http://localhost:8000”

在checkLogin.js里面请求user/info 来判定是或不是登录
继承完善 checkLogin

import {Component} from 'react';
import {withRouter} from 'react-router-dom'  // 由于checkLogin 不是路由,要想用跳转的话得引入withRouter

@withRouter
class CheckLogin extends Component {
    componentDidMount() {
        // 登录注册两个页面不需要判断
        if(filterCheck.indexOf(this.props.location.pathname) > -1) {
            return;
        }
         // 在这里请求相关接口判断用户是否完成登录
        axios.get('/user/info')
            .then(res => {
                if(res.status === 200) {
                    if(res.data.code === 0) {

                    }else {
                        this.props.history.push('/login')
                    }
                }
            })
    }
    render() {
        return null;
    }
}

export default CheckLogin;

中场休息完继续

由此地点的checkLogin,访问其余页面在没有登录的动静下,自动跳转到登录页面。

从登记页面开首完善
因为品种是组成redux来操作,所以登录注册都以浮动各类action,通过事先写好的user那个reducer来改变state的初步值

在登记页面写好register方法并导出给登记按钮调用,从而生成action,给reducer使用

// user.redux.js
import axios from 'axios'  // 用axios做请求

// 定义常量
const REGISTER_SUCCESS = 'REGISTER_SUCCESS'; // 注册成功
const TODO_ERRSHOW = 'TODO_ERRSHOW'; // 操作失败

// state初始值
let initState = {
    redirectTo: '', // 完成之后跳到哪里
    username: '', // 账号
    pwd: '', // 密码
    pwdConfirm: '', // 确认密码
    type: '', // 用户类型
    msg: '', // 错误消息
    isLogin: false // 是否登录
}

export function user(state=initState,action) {
    switch (action.type) {
       case REGISTER_SUCCESS:
            return {...state,...action.data, msg: '', redirectTo: '/login'}
       case TODO_ERRSHOW:
            return {...state,msg: action.msg}
       default:
            return state;
    }
}

function registerFail(msg) {
    return {
        msg,
        type: TODO_ERRSHOW
    }
}

function registerSuccess(data) {
    return {
        data,
        type: REGISTER_SUCCESS
    }
}

// register是一个action creator ,返回的action供user这个reducer使用,从而改变state
export function register({username,pwd,pwdConfirm,type}) {
    if(!username || !pwd) {
        registerFail('账号密码不能为空')
    }
    if(pwd !== pwdConfirm) {
        registerFail('两次密码不一致')
    }
    return dispatch => {
        axios.post('/user/register',{username,pwd,type})
            .then(res => {
                if(res.status === 200 && res.data.code === 0) {
                    dispatch(registerSuccess(res.data.data))
                }else {
                    dispatch(registerFail(res.data.msg))
                }
            })
    }
}

接下去在register.js里面包车型客车登记按钮绑定的风波调用register那一个情势

// register.js
import {connect} from 'react-redux'
import {register} from '../../redux/user.redux'
import Logo from '../../components/logo/logo'

@connect(
    state=> state,
    {register}
)

留意下面几行代码,在此之前在package.json提到的plugins
transform-decorators-legacy是为地方的connect装饰器支持@写法准备的。

装饰器connnect接受多少个参数,给props定义的多少和函数

// 注册按钮绑定的事件
/*
*   注册
* */
handleRegister() {
    this.props.register(this.state)
}

因为在register这几个函数里面,做了表单验证判断后起头请求“/user/register”这些接口,那么接下去完善这一个接口

报到注册的接口设计到数据库,那里接纳了mongoDB那种类型的数据库最为后台数据支持

新建数据模型

// server/model.js
const mongoose = require('mongoose')

// mongoose 连接这个数据库并生成"react-login-register"这个集合
mongoose.connect('mongodb://127.0.0.1:27017/react-login-register')

// 连接成功后的打印
mongoose.connection.on('connected',() => {
    console.log('mongo connect success');
})

// 创建数据模型
const models = {
    user: {
        'username': {type:String, require: true}, // 账号
        'pwd': {type:String, require: true}, // 密码
        'type': {type:String, require: true} // 用户类型
    }
}

// 遍历生成数据模型
for (let m in models) {
    mongoose.model(m, new mongoose.Schema(models[m]))
}

// 导出供其他地方获取
module.exports = {
    getModel: function (m) {
        return mongoose.model(m)
    }
}

// server/user.js
// 获取注册用户列表
Router.get('/list',(req,res) => {
    //清空所有用户
    // User.remove({},(err,doc) => {
    //  if(!err) {
    //      console.log(`用户清空成功`);
    //  }
    // })
    // 在user这个数据模型中查询所有用户
    User.find({},(err,doc) => {
        if(!err) {
            return res.json({code: 0, data: doc,msg: '用户列表获取成功'})
        }
    })
})

成就注册接口

// user.js
const express = require('express')
const model = require('./model')
const User = model.getModel('user')
// md5加密
const utils = require('utility')

// 生成express路由中间件
const Router = express.Router();

// 封装MD5加密规则
function my_md5(pwd) {
    const xiao = 'akdf352FHhjfFHI34=123-`.WRL23K23fhKJFHkhFJ@1231!*@%!^';
    return utils.md5(utils.md5(xiao + pwd))
}

//过滤调不想暴露的数据
const _filter = {"__v": 0, "pwd": 0}

// CheckLogin.js 用户查询用户是否登录的接口
Router.get('/info', (req, res) => {
    return res.json({code: 1, msg: '未登录'})
})

// 获取注册用户列表
Router.get('/list', (req, res) => {
    //清空所有用户
    // User.remove({}, (err, doc) => {
    //  if (!err) {
    //      console.log(`用户清空成功`);
    //  }
    // })

    // 在user这个数据模型中查询所有用户
    User.find({}, (err, doc) => {
        if (!err) {
            return res.json({code: 0, data: doc, msg: '用户列表获取成功'})
        }
    })
})

// 注册接口
Router.post('/register', (req, res) => {
    const {username, pwd, type} = req.body;
    // 在user这个数据模型中查询用户注册的账号是否存在
    User.findOne({username}, (err, doc) => {
        //
        if (doc) {
            return res.json({code: 1, msg: '用户已存在'})
        }
        if (err) {
            return res.json({code: 1, msg: '服务器异常'})
        }
        User.create({username, pwd: my_md5(pwd), type}, (err, doc) => {
            if (err) {
                return res.json({code: 1, msg: '服务器异常'})
            }
            return res.json({code: 0, data: doc})
        })
    })
})


module.exports = Router
// 这里注意,因为要接受参数,所以在server.js安装body-parser并app.use(bodyParser.json())

register.js

{this.props.user.redirectTo ? <Redirect to={this.props.user.redirectTo}></Redirect>:null} //注册完成后跳转到登录页面
<div className="err-show">{this.props.user.msg ? this.props.user.msg : ''}</div> //显示错误信息

时至前些天注册部分基本到位


继续

接下去形成报到部分

约莫思路是绑定事件

// login.js
/*
 *  登录
 * */
handleLogin() {
    this.props.login(this.state)
}

概念执行调用actions creator

// login.js
import {connect} from 'react-redux'

@connect (
    state => state,
    {login}
)

编写login actions creator

// user.redux.js
// 登录时候调用
export function login({username,pwd}) {
    if(!username || !pwd) {
        return toDoFail('账号密码不能为空')
    }
    return dispatch => {
        axios.post('/user/login',{username,pwd})
            .then(res => {
                if(res.status === 200 && res.data.code === 0) {
                    dispatch(loginSuccess(res.data.data))
                }else {
                    dispatch(toDoFail(res.data.msg))
                }
            })
    }
}

编写user/login那几个接口

 // user.js
 Router.post('/login', (req, res) => {
    const {username, pwd} = req.body;
    console.log(username);
    User.findOne({username, pwd: my_md5(pwd)}, _filter, (err, doc) => {
        if (!doc) {
            return res.json({code: 1, msg: '账号密码不正确'})
        }
        if (err) {
            return res.json({code: 1, msg: '服务器异常'})
        }
        res.cookie('userId',doc._id) // 登录成功保存cookie
        return res.json({code: 0, msg: '登录成功', data: doc})
    })
})

报到的步调和注册部分浩大相似之处
接下去,完善判断用户是或不是登录的接口,’/user/info’

// user.js
Router.get('/info', (req, res) => {
    const {userId} = req.cookies
    if (!userId) {
        res.json({code: 1, msg: '用户未登录'})
    }
    User.findOne({_id: userId}, _filter, (err, doc) => {
        if (err) {
            return res.json({code: 1, msg: '服务器异常'})
        }
        if (doc) {
            return res.json({code: 0, msg: '用户已登录',data: doc})
        }
    })
})
PS: server.js 记得使用cookie插件
const cookieParser = require('cookie-parser')
app.use(cookieParser())

终极,大家在报到成功之后所进入的页面显示用户的为主音信

// checkLogin.js
axios.get('/user/info')
  .then(res => {
      console.log(res);
      if(res.status === 200) {
          if(res.data.code === 0) {
              this.props.getUserInfo(res.data.data)
          }else {
              this.props.history.push('/login')
          }
      }
  })


// user.redux.js
// 登录之后获取用户信息
export function getUserInfo(userInfo) {
    return {
        type: GET_USER_INFO,
        payload: userInfo
    }    
}

对此次demo完善退出登录
跳转到login页面,清空cookies

// workerInfo.js
import React, {Component} from 'react';
import {connect} from 'react-redux'
import {Button,WingBlank} from 'antd-mobile'
import {Redirect} from 'react-router-dom'
import {loginOut} from '../../redux/user.redux'

@connect(
    state => state,
    {loginOut}
)

class WorkerInfo extends Component {
    render() {
        return (
            <div>
                {this.props.user.redirectTo ? <Redirect to={this.props.user.redirectTo}></Redirect>:null}
                <h3>worker信息页面</h3>
                <p>用户名称:{this.props.user.username} </p>
                <p>用户类型:{this.props.user.type} </p>
                <WingBlank>
                    <Button onClick={this.handleLoginOut.bind(this)} type="primary">退出登录</Button>
                </WingBlank>
            </div>
        );
    }
    /*
    *   退出登录
    * */
    handleLoginOut() {
        this.props.loginOut();
    }
}

export default WorkerInfo;

编纂退出登录action creator

// 退出登录调用
export function loginOut() {
    return dispatch => {
        axios.get('/user/loginOut')
                .then(res => {
                if (res.status === 200 && res.data.code === 0) {
                    dispatch(loginOutSuccess(res.data.data))
                } else {
                    dispatch(toDoFail(res.data.msg))
                }
            })
    }
}

编写退出登录接口

// user.js
// 退出登录
Router.get('/loginOut', (req, res) => {
    const {userId} = req.cookies;
    if(!userId) {
        res.json({code: 1, msg: '服务器异常'})
    }
    res.cookie('userId','');
    return res.json({code: 0, msg:'退出成功'})
})

到此整个demo写完

留住贰个报错音讯,可是对效果没有影响

登录或退出成功或注册成功开始展览页面跳转后,会报错
Warning: You tried to redirect to the same route you’re currently on:
“/workerInfo”
Warning: You tried to redirect to the same route you’re currently on:
“/bossInfo”
Warning: You tried to redirect to the same route you’re currently on:
“/login”
继承消除~~ 带理解~

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图
Copyright @ 2010-2019 美高梅手机版4858 版权所有