基于React Hooks的增删改查(CRUD)实例

1 效果展示

在这里插入图片描述
在这里插入图片描述

2 后台部分实现

2.1 创建Express项目

使用WebStorm创建Express项目server1,并且需要安装一些模块:

# sequelize模块在关系型数据库和对象之间做一个映射,操作数据库更加方便
npm install sequelize
# mysql2模块支持sequelize模块
npm install mysql2
# cors模块解决了跨域的问题
npm install cors

修改端口号,由于原本的3000端口号有些普遍,会与前端页面冲突,因此在bin文件夹下的www.js文件中,修改端口号。具体为第15行代码,将3000修改成8089

var port = normalizePort(process.env.PORT || '8089');

2.2 数据库部分

数据库部分与info数据库中的stu表进行连接,stu表的内容如下:
在这里插入图片描述
stu表数据结构如下:
在这里插入图片描述
1、在当前项目下新建文件夹config,在该文件夹下创建dbconfig.js文件,该文件是数据库的配置文件,用来连接数据库,具体代码如下:

var Sequelize = require("sequelize");
// 参数分别是:数据库名、用户名、密码
const DB = new Sequelize("info", "root", "123456", {
    host: "localhost", // 主机地址
    port: 3306, // 数据库端口号
    dialect: "mysql", // 数据库类型
    pool: { // 数据库连接池
        max: 5, // 最大连接数量
        min: 0, // 最小连接数量
        idle: 10000, // 如果10秒内没有被使用,释放该线程
    }
})

module.exports = DB;

2、在当前项目下新建文件夹Model,在该文件夹下新建文件stuModel.js,该文件用来建立数据库模型,将数据库的表映射到某个对象上,相当于与某张表建立联系,具体代码如下:

const DB = require("../config/dbconfig"); // 导入数据库配置文件
const Sequelize = require("sequelize"); // 导入模块
// stu是数据库的表名
const stuModel = DB.define("stu", {
    id: {
        primaryKey: true, // 设置为主键
        type: Sequelize.STRING, // 数据类型
        field: "sid", // 将表中的列名与对象名进行映射,当表名与对象名不同时使用
    },
    name: {
        type: Sequelize.STRING,
        allowNull: false, // 不允许为空
        field: "sname"
    },
    age: {
        type: Sequelize.INTEGER,
        allowNull: false
    },
    gender: {
        type: Sequelize.STRING,
        allowNull: false
    }
}, {
    freezeTableName: true, // 表示适应用户给定的表名
    timestamps: false // 不显示时间戳
})

module.exports = stuModel;

2.3 接口部分

1、在routes文件夹下新建文件student.js,配置自己的路由接口。在该文件中主要实现了增删改查的功能,具体文件如下:

var express = require("express");
var router = express.Router(); // 使用路由模块化管理
var stuModel = require("../Model/stuModel"); // 导入模型文件

// 获取所有学生信息:http://localhost:8089/student/search
router.get("/search", (req, res) => {
    stuModel.findAll({ // findAll,查询所有的数据
        raw: true // 显示时间戳
    }).then(result => {
        // 向前端发送JSON格式的数据,设置成功查询的code为1001
        res.json({
            code: 1001,
            msg: result
        })
    }).catch(err => {
        res.json({
            code: 1002, // 设置1002表示发生错误
            msg: err
        })
    })
})

// 添加学生信息:http://localhost:8089/student/add
router.post("/add", (req, res) => {
    stuModel.create({ // create表示创建信息
        id: req.body.id, // post请求使用req.body来获取
        name: req.body.name,
        age: req.body.age,
        gender: req.body.gender
    }).then(result => {
        res.json({
            code: 1001,
            msg: "插入成功"
        })
    }).catch(err => {
        res.json({
            code: 1002,
            msg: "插入失败"
        })
    })
})

// 删除学生信息:http://localhost:8089/student/delete
router.post("/delete", (req, res) => {
    let id = req.body.id; // 删除时首先要获取被删除的人的学号id再删除整个信息
    stuModel.destroy({ // destroy表示删除某个信息
        where: {
            id: id
        }
    }).then(result => {
        res.json({
            code: 1001,
            msg: "删除成功"
        })
    }).catch(err => {
        res.json({
            code: 1002,
            msg: "删除失败"
        })
    })
})

// 更新某条信息:http://localhost:8089/student/update
router.put("/update", (req, res) => {
    // 更新时先根据id查询到要更新的信息,然后再求改其他的信息,id信息不可修改
    stuModel.findOne({ // findOne表示查找某条信息
        where: {
            id: req.body.id
        }
    }).then(user => { // user表示查询到的结果
        user.update({ // update表示更新数据
            name: req.body.name,
            age: req.body.age,
            gender: req.body.gender
        }).then(result => {
            res.json({
                code: 1001,
                msg: "更新成功"
            })
        }).catch(err => {
            res.json({
                code: 1002,
                msg: "更新失败"
            })
        })
    }).catch(err => {
        res.json({
            code: 1002,
            msg: "查询失败"
        })
    })
})

module.exports = router;

2、在app.js文件中引入跨域模块,并根据刚刚写好的接口配置路由。

// 解决跨域
var cors = require("cors");
app.use(cors());
// 配置路由
var stuRouter = require('./routes/student');
app.use('/student', stuRouter);

3 前端部分实现

3.1 创建React项目

1、使用WebStorm创建React项目demo1,并且安装一些模块:

# axios模块用来发起请求
npm install axios

3.2 编写组件

2、在src文件夹下新建文件夹components,用来存放自定义的组件。在该文件夹下新建文件UserTable.js,该文件主要用来在网页上显示数据表格,具体代码如下:

import React from "react";

const UserTable = (props) => {
    return (
        // 边框为1
        <table border={1}>
            <thead>
            <tr>
                <th width={100}>学号</th>
                <th width={100}>姓名</th>
                <th width={100}>年龄</th>
                <th width={100}>性别</th>
                <th width={200} colSpan={2}>操作</th>
            </tr>
            </thead>
            <tbody>
            {/*显示数据库中多行信息,使用{}括起来*/}
            {
                // props.users表示从数据库中获取的所有信息,存放在数组中
                // 如果有信息显示所有信息
                props.users.length > 0 ? (
                    props.users.map(user => {
                        return (
                            <tr key={user.id}>
                                <td>{user.id}</td>
                                <td>{user.name}</td>
                                <td>{user.age}</td>
                                <td>{user.gender}</td>
                                <td>
                                    {/*点击编辑后将用户信息传到props下的editRow函数中*/}
                                    <button onClick={() => {
                                        props.editRow(user)
                                    }}>编辑
                                    </button>
                                    {/*点击删除后将用户id传给props下的deleteRow函数*/}
                                    <button onClick={() => {
                                        props.deleteRow(user.id)
                                    }}>删除
                                    </button>
                                </td>
                            </tr>
                        )
                    })
                ) : (
                    // 如果没有信息则显示没有用户信息
                    <tr>
                        <td colSpan={5}>没有用户信息</td>
                    </tr>
                )
            }
            </tbody>
        </table>
    )
}

export default UserTable;

在这里插入图片描述

3、在components文件夹下新建文件AddUserForm.js,用来编写添加用户信息的表单,具体代码如下:

import React, {useState} from "react";

const AddUserForm = (props) => {
    const initFormState = {id: " ", name: " ", age: " ", gender: " "};
    // 将初始的stu设置为空
    const [stu, setStu] = useState(initFormState);

    // 定义获取input值的方法
    const handleInputChange = (event) => {
        const {name, value} = event.target; // event.target获取到了当前input输入的值
        setStu({...stu, [name]: value}); // 将修改后的值存放到stu中
    }

    return (
        <form onSubmit={(event => {
            event.preventDefault(); // 屏蔽默认提交
            if (!stu.id || !stu.name || !stu.age || !stu.gender) {
                return; // 如果四项有一项为空,则返回
            }
            props.addRow(stu); // 将当前的stu传入到props下的addRow方法中
            setStu(initFormState); // 传入结束再让添加显示为空
        })}>
            <label>
                学号:<input type={"text"} name={"id"} value={stu.id} onChange={handleInputChange}/>
            </label>
            <br/><br/>
            <label>
                姓名:<input type={"text"} name={"name"} value={stu.name} onChange={handleInputChange}/>
            </label>
            <br/><br/>
            <label>
                年龄:<input type={"text"} name={"age"} value={stu.age} onChange={handleInputChange}/>
            </label>
            <br/><br/>
            <label>
                性别:<input type={"text"} name={"gender"} value={stu.gender} onChange={handleInputChange}/>
            </label>
            <br/><br/>
            <button>添加信息</button>
        </form>
    )
}

export default AddUserForm;

在这里插入图片描述
4、在src文件夹下新建文件EditUserForm.js,该文件主要编写了更新信息的页面,具体代码如下:

import React, {useEffect, useState} from "react";

const EditUserForm = (props) => {
    // props.currentStu是传给EditUserForm的参数,是自定义的
    const [stu, setStu] = useState(props.currentStu);
    // 当props发生改变则设置当前状态
    useEffect(() => {
        setStu(props.currentStu);
    }, [props]);
    // 当输入框中的内容发生改变时,则修改当前的状态
    const handleInputChange = (event) => {
        const {name, value} = event.target; // 获取正在修改的输入框的值
        setStu({...stu, [name]: value});
    }

    return (
        <form onSubmit={(event) => {
            event.preventDefault(); // 阻止默认提交
            props.updateRow(stu); // 为props中的updateRow方法传入参数stu
        }}>
            <label>
                学号:<input type={"text"} name={"id"} value={stu.id} disabled onChange={handleInputChange}/>
            </label>
            <br/><br/>
            <label>
                姓名:<input type={"text"} name={"name"} value={stu.name} onChange={handleInputChange}/>
            </label>
            <br/><br/>
            <label>
                年龄:<input type={"text"} name={"age"} value={stu.age} onChange={handleInputChange}/>
            </label>
            <br/><br/>
            <label>
                性别:<input type={"text"} name={"gender"} value={stu.gender} onChange={handleInputChange}/>
            </label>
            <br/><br/>
            <button>更新学生</button>
            &nbsp;&nbsp;&nbsp;
            {/*点击按钮将props下的setEditing状态改成false*/}
            <button onClick={() => props.setEditing(false)}>取消更新</button>
        </form>
    )
}

export default EditUserForm;

在这里插入图片描述
5、编写App.js文件,在该文件中编写CRUD操作,以及将组件集合起来,具体代码如下:

import logo from './logo.svg';
import './App.css';
import UserTable from "./components/UserTable";
import AddUserForm from "./components/AddUserForm";
import EditUserForm from "./components/EditUserForm";
import {useEffect, useState, Fragment} from "react";
import axios from "axios";

function App() {
    const initFormState = {id: " ", name: " ", age: " ", gender: " "};
    const [student, setStudent] = useState(initFormState); // 将初始值传给当前的student
    const [editing, setEditing] = useState(false); // 设置初识editing为false
    const [currentStu, setCurrentStu] = useState(initFormState); // 设置初始为空

    // CRUD操作
    // 查询所有学生
    const getRows = () => {
        axios.get("http://localhost:8089/student/search")
            .then(res => { // res是请求成功后服务器返回的响应数据,这里是查询出来的所有人的信息
                setStudent(res.data.msg); // 设置当前学生信息为传回来的结果,msg是在后端定义的
            }).catch(err => {
            console.log(err);
        })
    }

    // 添加学生信息
    const addRow = (stu) => {
        axios.post("http://localhost:8089/student/add", stu)
            .then(res => {
                getRows(); // 请求成功后重新获取数据库中的信息
            }).catch(err => {
            console.log(err);
        })
    }

    // 删除学生信息
    const deleteRow = (id) => {
        setEditing(false);
        axios.delete("http://localhost:8089/student/delete", {
            data: {
                id: id
            }
        }).then(res => {
            getRows(); // 删除成功后重新获取数据库中的数据
        })
    }

    // 修改学生信息
    const updateRow = (stu) => {
        setEditing(false);
        axios.put("http://localhost:8089/student/update", stu)
            .then(res => {
                getRows(); // 更新成功后重新获取数据库中的信息
            }).catch(err => {
            console.log(err);
        })
    }

    const editRow = (stu) => {
        setEditing(true);
        setCurrentStu({
            id: stu.id,
            name: stu.name,
            age: stu.age,
            gender: stu.gender
        })
    }

    // 当editing发生改变时获取所有学生信息
    useEffect(() => {
        getRows();
    }, [editing]);

    return (
        <div className="App">
            <header className="App-header">
                <img src={logo} className="App-logo" alt="logo"/>
            </header>
            <h1>CURD App With Hooks</h1>
            <div className={"flex-row"}>
                <div className={"flex-large"}>
                    {
                        editing ? (
                            <Fragment>
                                <h2>编辑学生</h2>
                                <EditUserForm currentStu={currentStu} updateRow={updateRow} setEditing={setEditing}/>
                            </Fragment>
                        ) : (
                            <Fragment>
                                <h2>添加学生</h2>
                                <AddUserForm addRow={addRow}/>
                            </Fragment>
                        )
                    }
                </div>
                <div className={"flex-large"}>
                    <h2>View Students</h2>
                    <UserTable users={student} editRow={editRow} deleteRow={deleteRow}/>
                </div>
            </div>
        </div>
    );
}

export default App;

6、编写App.css文件,设置样式,代码如下:

.App {
    text-align: center;
}

.App-logo {
    height: 10vmin;
    pointer-events: none;
}

@media (prefers-reduced-motion: no-preference) {
    .App-logo {
        animation: App-logo-spin infinite 20s linear;
    }
}

.App-header {
    background-color: #282c34;
    min-height: 10vh;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    font-size: calc(10px + 2vmin);
    color: white;
}

@keyframes App-logo-spin {
    from {
        transform: rotate(0deg);
    }
    to {
        transform: rotate(360deg);
    }
}

.flex-row {
    display: flex;
}

.flex-large {
    margin-left: 300px;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值