react-hooks + redux实现一个完整的的前后端登录注册小案例

因为之前写项目一直没有用状态管理,打算在下个项目里用hooksreact-redux。所以最近学了一下reduxhooks,写了一个登录注册来练习练习。

前端:react + antd + react-router-dom +react-redux + react-hooks

后端:Node + MongoDB

1、前端所需第三方依赖:

在这里插入图片描述

2、后端所需第三方依赖

在这里插入图片描述

该案例我配置了antd的按需加载以及less语法的使用。

一、项目目录

该案例共分为clientserver两个文件夹。

1、client文件夹

入口文件:index.js

1、api/ajax : 封装的ajax请求函数
2、api/index: 包含n个接口请求的函数模块
(函数返回值是promise对象)
在这里插入图片描述

2、server文件夹

入口文件:www.js

1、bin/www.js: 后端入口文件
2、 app.js: 配置文件
3、routes/index: 后端路由文件
在这里插入图片描述

3、前端静态页面的搭建

使用了antd组件库搭建静态页面.
效果图如下
在这里插入图片描述
在这里插入图片描述

4、redux的搭建

针对于这一步我觉得大家有一定的基础都没问题,所以我就不贴代码了。

5、login组件

import React, { useState}from 'react';
import { Form, Input, Button,message} from 'antd'
import { UserOutlined, LockOutlined } from '@ant-design/icons';
import { Link } from 'react-router-dom'
import {connect} from 'react-redux'

import '../../assets/css/login.less'
import {login} from '../../redux/actions'

const FormItem = Form.Item;
function Login(props) {


    const [username, setUsername] = useState('');
    const [ password , setPassword ] = useState('');

  const  onFinish = () => {

      
     
         if (!username || !password) {
           return message.error('未输入用户名或密码')
         }
     
    props.login({username, password})
    }
    
    const{ msg} = props.user
    return (
        <div className='loginForm'>
        <div className='auth-form-header'>
        <h1 className='login-title'>Sign in</h1>
        </div>
        
        {msg ? <div className='error-msg'>{msg}</div> : null}
<Form
name="normal_login"
className="login-form"
initialValues={{
remember: true,
      }} 
onFinish={onFinish}
>
<FormItem
// name="username"
rules={[
  {
    required: true,
    message: '请输入用户名!',
  },
        ]}        
>
        <Input
          defaultValue={username}
          name="username"
          prefix={<UserOutlined className="site-form-item-icon" />}
          placeholder="Username"
          // defaultValue={username}
          onChange={(e)=>{setUsername(e.target.value)}} />
</FormItem>
            
<FormItem
// name="password"
rules={[
  {
    required: true,
    message: '请输入密码!',
  },
        ]}
 
>
        <Input
     defaultValue={password}
  prefix={<LockOutlined className="site-form-item-icon" />}
  type="password"
          placeholder="Password"
          name='password'
          // defaultValue={password}
          onChange={(e)=>{setPassword(e.target.value)}}   
/>
</FormItem>


<FormItem>
<Button type="primary" htmlType="submit" className="login-form-button"  >
  登录
</Button>
<div className='register-prompt'>
  <p>没有账号?
 {/* <a href='/register' className='register-text'>去注册</a> */}
 <Link to='/register'>去注册</Link>                      
  </p>
         
</div>
</FormItem>
</Form>
    </div>
    )
}

export default connect(
  state => ({user:state.user}),
  {login}
)(Login)

5、register组件

import React, { useState, }from 'react';
import { Form, Input, Button, message } from 'antd'
import { Link } from 'react-router-dom'
import {connect ,}from 'react-redux'

import '../../assets/css/register.less'
import {register} from '../../redux/actions'


const formItemLayout = {
    labelCol: {
      xs: {
        span: 18,
      },
      sm: {
        span: 7,
      },
    },
    wrapperCol: {
      xs: {
        span: 18,
      },
      sm: {
        span: 18,
      },
    },
};

const tailFormItemLayout = {
wrapperCol: {
  xs: {
    span: 24,
    offset: 0,
  },
  sm: {
    span: 10,
    offset: 7,
  },
},
};


function Register(props) {

    const [username, setUsername] = useState('')
    const [password, setPassword] = useState('')
  const [confirmpassword, setconfirmpassword] = useState('')
  
 
    
  // const dispatch = useDispatch();

  const onFinish = () => {
    
        if (!username || !password || !confirmpassword) {
          return message.error('未输入用户名或密码')
        }
    

      // dispatch(register({username, password}))
    props.register({username, password})
    }
    

    const{ msg} = props.user

    return (
        <div className='RegisterForm'>
                <div className='auth-form-header'>
                <h1 className='register-title'>Sign up </h1>
        </div>
        
        {msg ? <div className='error-msg'>{msg}</div> : null}

          <Form
              {...formItemLayout}
      name="normal_login"
      className="register-form"
      initialValues={{
        remember: true,
      }}
      onFinish={onFinish}
    >
     <Form.Item
        name="username"
        label="用户名"
        // tooltip="What do you want others to call you?"
        rules={[
          {
            required: true,
            message: '请输入用户名!',
            whitespace: true,
          },
        ]}
      >
              <Input name='username' onChange={ (e)=>{setUsername(e.target.value)}}/>
      </Form.Item>
                    
     <Form.Item
        name="password"
        label="输入密码"
        rules={[
          {
            required: true,
            message: '请输入密码!',
          },
        ]}
        hasFeedback
      >
        <Input.Password name='password' onChange={ (e)=>{setPassword(e.target.value)}}/>
      </Form.Item>
              
        <Form.Item
        name="confirmpassword"
        label="确认密码"
        dependencies={['password']}
        hasFeedback
        rules={[
          {
            required: true,
            message: '请确认密码!',
          },
          ({ getFieldValue }) => ({
            validator(_, value) {
              if (!value || getFieldValue('password') === value) {
                return Promise.resolve();
              }

              return Promise.reject(new Error('两次输入密码不一致!'));
            },
          }),
        ]}
      >
        <Input.Password name="confirmpassword" onChange={ (e)=>{setconfirmpassword(e.target.value)}}/>
            </Form.Item>
            
       <Form.Item {...tailFormItemLayout} >
        <Button type="primary" htmlType="submit" className='register-form-button'>
                注册
        </Button>
              

            </Form.Item>
            
            <div className='login-prompt'>
     
     <p>
                        已有账号? &nbsp;
    {/* <a href='/login' className='login-text'>去登录</a>  */}
      <Link to='/login'>去登录</Link> 
     </p>        
    </div>
              
    </Form>
            
      </div>
    )
}

export default connect (
  state => ({user: state.user}),
  {register}
)(Register)

后端node环境搭建

1、入口文件www.js

// 后端入口文件

var app = require('../app')

var http = require('http')

var server = http.createServer(app)


// server.listen(4000)
server.listen(4000, () => {
    console.log('server is running at port 8000 success~~~');
})




2、app.js

// 后端配置
const bodyParser = require('body-parser')
const express = require('express')
const router = require('./routes/index')


var app = express()

const db = require('./db/connect')  //连接数据库

// 配置解析表单 POST 请求体插件(注意:一定要在 app.use(router) 之前 )
// parse application/x-www-form-urlencoded
app.use(bodyParser.urlencoded({ extended: false }))
// parse application/json
app.use(bodyParser.json())


app.use(router)


module.exports = app

3、connect.js 连接数据库文件

const mongoose = require('mongoose')

// 连接数据库
mongoose.connect('mongodb://localhost/usertest', { useNewUrlParser: true, useUnifiedTopology: true })

var db = mongoose.connection // 数据库的链接对象
db.on('error', console.error.bind(console, 'connection error:'))
db.once('open', function() {
  console.log('数据库链接成功')
  // we're connected!
})

userModels.js 数据表的设计

const mongoose = require('mongoose')

var Schema = mongoose.Schema

const userSchema = new Schema({
    username: {
        type: String
    },
    password: {
        type: String
    },
   
})

const UserModel = mongoose.model('user', userSchema)

exports.UserModel = UserModel

routes/index.js 路由文件

var express = require('express')
var router = express.Router()
const md5 = require('blueimp-md5') //md5加密的函数
const { UserModel } = require('../db/Models/userModel')
const filter = {password:0,__v:0}  //查询时过滤出指定的属性

// 用户注册
router.post('/register', (req, res) => {
    const { username, password } = req.body
    
    UserModel.findOne({ username }, (err, user) => {
        if (user) {
            res.send({code:1, msg:'此用户已存在'})
        }
        else {
            new UserModel({ username, password: md5(password) }).save((err, user) => {
                if (err) {
                    console.log(err);
                }
                else {
                      // 生成一个 cookie(userid: user._id), 并交给浏览器保存
                 res.cookie('userid', user._id, { maxAge: 1000 * 60 * 60 * 24 * 7 }) // 持久化 cookie, 浏`览器会保存在本地文件
                 // 返回包含user的json数据
                 const data = {username, _id:user._id,msg:'注册成功!'}  //响应数据中不要携带password
                 res.send({code:0 ,data})
                }
            })
        }
    })
})


// 用户登录
router.post('/login', (req, res) => {
    const { username, password } = req.body

    UserModel.findOne({ username, password: md5(password) }, filter, (err, user) => {
        if (user) {
            res.cookie('userid', user._id, { maxAge: 1000 * 60 * 60 * 24 * 7 }) // 持久化 cookie, 浏 览器会保存在本地文件

            const data = {username,_id:user._id}
            res.send({code:0, data,msg:'登录成功!'})
        }
        else {
            res.send({code:1, msg:'用户名或密码不正确'})
        }
    })
    
})





module.exports = router;

下面关于redux的一些业务逻辑我只贴关键的action和reducer代码。

1、 actions.js

import {
    AUTH_SUCCESS,
    ERROR_MSG,
 
} from './action-types'

import {
    reqRegister,
    reqLogin,
}from '../api/index'

//授权成功的同步action
const authSuccess = (user,msg) => ({type:AUTH_SUCCESS,data:{user,msg}})
//错误提示信息的同步action
const errorMsg = (msg) => ({type:ERROR_MSG,data:msg})



// 注册异步action
export const register = (user) => {
    const { username, password } = user
    
    return async dispatch => {
        const response = await reqRegister({ username, password })
        const result = response.data
       
        if (result.code === 0) {
            dispatch(authSuccess(result.data))
        }
        else {
            dispatch(errorMsg(result.msg))
        }
    }
}

// 登录异步action、
export const login = (user) => {
    const { username, password } = user
    
    return async dispatch => {
        const response = await reqLogin({ username, password })
        const result = response.data

        // console.log(result);
        if (result.code === 0) {
            dispatch(authSuccess(result.data,result.msg))
        }
        else {
            dispatch(errorMsg(result.msg))
        }
    }
}

2、reducers.js

import { combineReducers } from 'redux'
import {
    AUTH_SUCCESS,
    ERROR_MSG,
  
} from './action-types'

const initUser = {
    username:'',//用户名
    msg:'',//错误提示信息
  
}

function user(state = initUser, action) {
    switch (action.type){
        case AUTH_SUCCESS:
            return { ...action.data }
        case ERROR_MSG:
            return { ...state, msg: action.data }
        default:
            return state
    }
  
}

export default combineReducers(
    {
        user,
    }
)

至此,一个完整的前后端登录注册就已经成功了。

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值