📌  相关文章
📜  如何使用 Node.js 和 ReactJS 构建一个基本的 CRUD 应用程序?

📅  最后修改于: 2022-05-13 01:56:46.032000             🧑  作者: Mango

如何使用 Node.js 和 ReactJS 构建一个基本的 CRUD 应用程序?

在本文中,我们将从头开始创建一个基本的学生应用程序。

应用功能:

  • 创建一个新学生
  • 更新现有学生
  • 显示学生名单
  • 删除学生

此项目中的 REST API:

REST API 

URL

GEThttp://localhost:4000/students
GET/students/update-student/id
POST/students/create-student
PUT/students/update-student/id
DELETE/students/delete-student/id

首先,我们将使用 React.js 处理应用程序的前端部分。

创建 React 应用程序并安装模块

第 1 步:让我们开始使用 React 构建前端部分。要创建一个新的 React 应用程序,请在终端中输入以下代码并按 Enter。

npx create-react-app mern-stack-crud

第 2 步:进入 React 项目文件夹。

cd mern-stack-crud

第 3 步:要运行 React 应用程序,请运行以下命令:

npm start

此命令通过以下 URL 将 React 应用程序打开到浏览器: http://localhost:3000/

第 4 步:要构建 React 应用程序,我们需要安装一些外部模块。

NPM

Detail

React-BootstrapReact-Bootstrap has evolved and grown alongside React, making it an excellent choice for your UI.
React-Router-DomReact Router DOM enables you to implement routing in a React App. 
AxiosIt is a promise base HTTP Client and use for network request.
FormikA great library to build form in React.
YupYup is a JavaScript schema builder for form validation.

要安装,请在终端上运行以下代码。

第 5 步:创建简单的 React 组件——在这一步中,我们将创建一些 React 组件来管理学生数据。

前往src文件夹,创建一个文件夹并将其命名为Components ,然后在该目录中创建以下组件。

  • StudentForm.js - 可重复使用的学生表单
  • create-student.component.js - 负责创建新学生
  • edit-student.component.js – 负责更新学生数据
  • student-list.component.js - 负责显示所有学生
  • StudentTableRow.js - 负责显示单个学生

项目结构:它将如下所示

前端项目结构

第 6 步:创建学生表单——在这一步中,我们将使用 Formik 和 React-Bootstrap 构建一个可重用的学生表单。此表格包含输入学生详细信息的所有必要字段。我们还使用 Yup 进行了客户端表单验证。将来,我们将使用此组件来创建和更新学生。转到src/Components/StudentForm.js并编写以下代码。

StudentForm.js
import React from "react";
import * as Yup from "yup";
import { Formik, Form, Field, ErrorMessage } from "formik";
import { FormGroup, FormControl, Button } from "react-bootstrap";
  
const StudentForm = (props) => {
  const validationSchema = Yup.object().shape({
    name: Yup.string().required("Rquired"),
    email: Yup.string()
      .email("You have enter an invalid email address")
      .required("Rquired"),
    rollno: Yup.number()
      .positive("Invalid roll number")
      .integer("Invalid roll number")
      .required("Rquired"),
  });
  console.log(props);
  return (
    
               
                                                                                                                                                                   
      
    
  ); };    export default StudentForm;


create-student.component.js
// CreateStudent Component for add new student
  
// Import Modules
import React, { useState, useEffect } from "react";
import axios from 'axios';
import StudentForm from "./StudentForm";
  
// CreateStudent Component
const CreateStudent = () => {
  const [formValues, setFormValues] = 
    useState({ name: '', email: '', rollno: '' })
  // onSubmit handler
  const onSubmit = studentObject => {
    axios.post(
'http://localhost:4000/students/create-student', 
    studentObject)
      .then(res => {
        if (res.status === 200)
          alert('Student successfully created')
        else
          Promise.reject()
      })
      .catch(err => alert('Something went wrong'))
  }
    
  // Return student form
  return(
    
      Create Student
    
  )
}
  
// Export CreateStudent Component
export default CreateStudent


edit-student.component.js
// EditStudent Component for update student data
  
// Import Modules
import React, { useState, useEffect } from "react";
import axios from "axios";
import StudentForm from "./StudentForm";
  
// EditStudent Component
const EditStudent = (props) => {
  const [formValues, setFormValues] = useState({
    name: "",
    email: "",
    rollno: "",
  });
    
  //onSubmit handler
  const onSubmit = (studentObject) => {
    axios
      .put(
        "http://localhost:4000/students/update-student/" +
          props.match.params.id,
        studentObject
      )
      .then((res) => {
        if (res.status === 200) {
          alert("Student successfully updated");
          props.history.push("/student-list");
        } else Promise.reject();
      })
      .catch((err) => alert("Something went wrong"));
  };
  
  // Load data from server and reinitialize student form
  useEffect(() => {
    axios
      .get(
        "http://localhost:4000/students/update-student/" 
        + props.match.params.id
      )
      .then((res) => {
        const { name, email, rollno } = res.data;
        setFormValues({ name, email, rollno });
      })
      .catch((err) => console.log(err));
  }, []);
  
  // Return student form
  return (
    
      Update Student
    
  );
};
  
// Export EditStudent Component
export default EditStudent;


student-list.component.js
import React, { useState, useEffect } from "react";
import axios from "axios";
import { Table } from "react-bootstrap";
import StudentTableRow from "./StudentTableRow";
  
const StudentList = () => {
  const [students, setStudents] = useState([]);
  
  useEffect(() => {
    axios
      .get("http://localhost:4000/students/")
      .then(({ data }) => {
        setStudents(data);
      })
      .catch((error) => {
        console.log(error);
      });
  }, []);
  
  const DataTable = () => {
    return students.map((res, i) => {
      return ;
    });
  };
  
  return (
    
                                                                                                           {DataTable()}       
NameEmailRoll NoAction
    
  ); };    export default StudentList;


StudentTableRow.js
import React from "react";
import { Button } from "react-bootstrap";
import { Link } from "react-router-dom";
import axios from "axios";
  
const StudentTableRow = (props) => {
  const { _id, name, email, rollno } = props.obj;
  
  const deleteStudent = () => {
    axios
      .delete(
"http://localhost:4000/students/delete-student/" + _id)
      .then((res) => {
        if (res.status === 200) {
          alert("Student successfully deleted");
          window.location.reload();
        } else Promise.reject();
      })
      .catch((err) => alert("Something went wrong"));
  };
  
  return (
    
      {name}
      {email}
      {rollno}
      
        
          Edit
        
        
      
    
  );
};
  
export default StudentTableRow;


App.js
// Import React
import React from "react";
  
// Import Bootstrap
import { Nav, Navbar, Container, Row, Col } 
        from "react-bootstrap";
import "bootstrap/dist/css/bootstrap.css";
  
// Import Custom CSS
import "./App.css";
  
// Import from react-router-dom
import { BrowserRouter as Router, Switch,
    Route, Link } from "react-router-dom";
  
// Import other React Component
import CreateStudent from 
    "./Components/create-student.component";
import EditStudent from 
    "./Components/edit-student.component";
import StudentList from 
    "./Components/student-list.component";
  
// App Component
const App = () => {
  return (
    
      
        
                                                                          React MERN Stack App                                                                                   
                                                  
                                                                                                                            
                       
        
      
    
  ); };    export default App;


App.css
.wrapper {
  padding-top: 30px;
}
  
body h3 {
  margin-bottom: 25px;
}
  
.navbar-brand a {
  color: #ffffff;
}
  
.form-wrapper,
.table-wrapper {
  max-width: 500px;
  margin: 0 auto;
}
  
.table-wrapper {
  max-width: 700px;
}
  
.edit-link {
  padding: 7px 10px;
  font-size: 0.875rem;
  line-height: normal;
  border-radius: 0.2rem;
  color: #fff;
  background-color: #28a745;
  border-color: #28a745;
  margin-right: 10px;
  position: relative;
  top: 1px;
}
  
.edit-link:hover {
  text-decoration: none;
  color: #ffffff;
}
  
/* Chrome, Safari, Edge, Opera */
input::-webkit-outer-spin-button,
input::-webkit-inner-spin-button {
  -webkit-appearance: none;
  margin: 0;
}
  
/* Firefox */
input[type=number] {
  -moz-appearance: textfield;
}


db.js
module.exports = {
  db: 'mongodb://localhost:27017/reactdb'
};


Student.js
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
  
let studentSchema = new Schema({
  name: {
    type: String
  },
  email: {
    type: String
  },
  rollno: {
    type: Number
  }
}, {
    collection: 'students'
  })
  
module.exports = mongoose.model('Student', studentSchema)


student.route.js
let mongoose = require("mongoose"),
  express = require("express"),
  router = express.Router();
  
// Student Model
let studentSchema = require("../models/Student");
  
// CREATE Student
router.post("/create-student", (req, res, next) => {
  studentSchema.create(req.body, (error, data) => {
    if (error) {
      return next(error);
    } else {
      console.log(data);
      res.json(data);
    }
  });
});
  
// READ Students
router.get("/", (req, res) => {
  studentSchema.find((error, data) => {
    if (error) {
      return next(error);
    } else {
      res.json(data);
    }
  });
});
  
// UPDATE student
router
  .route("/update-student/:id")
  // Get Single Student
  .get((req, res) => {
    studentSchema.findById(
        req.params.id, (error, data) => {
      if (error) {
        return next(error);
      } else {
        res.json(data);
      }
    });
  })
  
  // Update Student Data
  .put((req, res, next) => {
    studentSchema.findByIdAndUpdate(
      req.params.id,
      {
        $set: req.body,
      },
      (error, data) => {
        if (error) {
          return next(error);
          console.log(error);
        } else {
          res.json(data);
          console.log("Student updated successfully !");
        }
      }
    );
  });
  
// Delete Student
router.delete("/delete-student/:id", 
(req, res, next) => {
  studentSchema.findByIdAndRemove(
      req.params.id, (error, data) => {
    if (error) {
      return next(error);
    } else {
      res.status(200).json({
        msg: data,
      });
    }
  });
});
  
module.exports = router;


server.js
let express = require('express');
let mongoose = require('mongoose');
let cors = require('cors');
let bodyParser = require('body-parser');
let dbConfig = require('./database/db');
  
// Express Route
const studentRoute = require('../backend/routes/student.route')
  
// Configure mongoDB Database
mongoose.set('useNewUrlParser', true);
mongoose.set('useFindAndModify', false);
mongoose.set('useCreateIndex', true);
mongoose.set('useUnifiedTopology', true);
  
// Connecting MongoDB Database
mongoose.Promise = global.Promise;
mongoose.connect(dbConfig.db).then(() => {
  console.log('Database successfully connected!')
},
  error => {
    console.log('Could not connect to database : ' + error)
  }
)
  
const app = express();
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({
  extended: true
}));
app.use(cors());
app.use('/students', studentRoute)
  
  
// PORT
const port = process.env.PORT || 4000;
const server = app.listen(port, () => {
  console.log('Connected to port ' + port)
})
  
// 404 Error
app.use((req, res, next) => {
  res.status(404).send('Error 404!')
});
  
app.use(function (err, req, res, next) {
  console.error(err.message);
  if (!err.statusCode) err.statusCode = 500;
  res.status(err.statusCode).send(err.message);
});


第 7 步:创建一个新学生:在这一步中,我们将创建一个组件来添加一个新学生。我们已经创建了一个StudentForm组件来输入学生的详细信息。现在,是时候使用这个组件了。转到src/Components/create-student.component.js并编写以下代码。

创建-student.component.js

// CreateStudent Component for add new student
  
// Import Modules
import React, { useState, useEffect } from "react";
import axios from 'axios';
import StudentForm from "./StudentForm";
  
// CreateStudent Component
const CreateStudent = () => {
  const [formValues, setFormValues] = 
    useState({ name: '', email: '', rollno: '' })
  // onSubmit handler
  const onSubmit = studentObject => {
    axios.post(
'http://localhost:4000/students/create-student', 
    studentObject)
      .then(res => {
        if (res.status === 200)
          alert('Student successfully created')
        else
          Promise.reject()
      })
      .catch(err => alert('Something went wrong'))
  }
    
  // Return student form
  return(
    
      Create Student
    
  )
}
  
// Export CreateStudent Component
export default CreateStudent

第 8 步:更新学生的详细信息:在本节中,我们将创建一个组件来更新详细信息。我们有可重用的StudentForm组件,让我们再次使用它。我们将获取学生详细信息以重新初始化表格。转到src/Components/edit-student.component.js并编写以下代码。

编辑-student.component.js

// EditStudent Component for update student data
  
// Import Modules
import React, { useState, useEffect } from "react";
import axios from "axios";
import StudentForm from "./StudentForm";
  
// EditStudent Component
const EditStudent = (props) => {
  const [formValues, setFormValues] = useState({
    name: "",
    email: "",
    rollno: "",
  });
    
  //onSubmit handler
  const onSubmit = (studentObject) => {
    axios
      .put(
        "http://localhost:4000/students/update-student/" +
          props.match.params.id,
        studentObject
      )
      .then((res) => {
        if (res.status === 200) {
          alert("Student successfully updated");
          props.history.push("/student-list");
        } else Promise.reject();
      })
      .catch((err) => alert("Something went wrong"));
  };
  
  // Load data from server and reinitialize student form
  useEffect(() => {
    axios
      .get(
        "http://localhost:4000/students/update-student/" 
        + props.match.params.id
      )
      .then((res) => {
        const { name, email, rollno } = res.data;
        setFormValues({ name, email, rollno });
      })
      .catch((err) => console.log(err));
  }, []);
  
  // Return student form
  return (
    
      Update Student
    
  );
};
  
// Export EditStudent Component
export default EditStudent;

第 9 步:显示学生列表:在这一步中,我们将构建一个组件以在表格中显示学生详细信息。我们将获取学生的数据并对其进行迭代以为每个学生创建表行。转到src/Components/student-list.component.js并编写以下代码。

学生列表.component.js

import React, { useState, useEffect } from "react";
import axios from "axios";
import { Table } from "react-bootstrap";
import StudentTableRow from "./StudentTableRow";
  
const StudentList = () => {
  const [students, setStudents] = useState([]);
  
  useEffect(() => {
    axios
      .get("http://localhost:4000/students/")
      .then(({ data }) => {
        setStudents(data);
      })
      .catch((error) => {
        console.log(error);
      });
  }, []);
  
  const DataTable = () => {
    return students.map((res, i) => {
      return ;
    });
  };
  
  return (
    
                                                                                                           {DataTable()}       
NameEmailRoll NoAction
    
  ); };    export default StudentList;

步骤 10:显示单个学生:在这一步中,我们将返回负责显示学生数据的表行。转到src/Components/StudentTableRow.js并编写以下代码。

StudentTableRow.js

import React from "react";
import { Button } from "react-bootstrap";
import { Link } from "react-router-dom";
import axios from "axios";
  
const StudentTableRow = (props) => {
  const { _id, name, email, rollno } = props.obj;
  
  const deleteStudent = () => {
    axios
      .delete(
"http://localhost:4000/students/delete-student/" + _id)
      .then((res) => {
        if (res.status === 200) {
          alert("Student successfully deleted");
          window.location.reload();
        } else Promise.reject();
      })
      .catch((err) => alert("Something went wrong"));
  };
  
  return (
    
      {name}
      {email}
      {rollno}
      
        
          Edit
        
        
      
    
  );
};
  
export default StudentTableRow;

第 11 步:编辑 App.js:最后,在我们的 MERN Stack CRUD 应用程序中包含用于进行路由的菜单。转到src/App.js并编写以下代码。

应用程序.js

// Import React
import React from "react";
  
// Import Bootstrap
import { Nav, Navbar, Container, Row, Col } 
        from "react-bootstrap";
import "bootstrap/dist/css/bootstrap.css";
  
// Import Custom CSS
import "./App.css";
  
// Import from react-router-dom
import { BrowserRouter as Router, Switch,
    Route, Link } from "react-router-dom";
  
// Import other React Component
import CreateStudent from 
    "./Components/create-student.component";
import EditStudent from 
    "./Components/edit-student.component";
import StudentList from 
    "./Components/student-list.component";
  
// App Component
const App = () => {
  return (
    
      
        
                                                                          React MERN Stack App                                                                                   
                                                  
                                                                                                                            
                       
        
      
    
  ); };    export default App;

第 12 步:添加样式 -转到src/App.css并编写以下代码。

应用程序.css

.wrapper {
  padding-top: 30px;
}
  
body h3 {
  margin-bottom: 25px;
}
  
.navbar-brand a {
  color: #ffffff;
}
  
.form-wrapper,
.table-wrapper {
  max-width: 500px;
  margin: 0 auto;
}
  
.table-wrapper {
  max-width: 700px;
}
  
.edit-link {
  padding: 7px 10px;
  font-size: 0.875rem;
  line-height: normal;
  border-radius: 0.2rem;
  color: #fff;
  background-color: #28a745;
  border-color: #28a745;
  margin-right: 10px;
  position: relative;
  top: 1px;
}
  
.edit-link:hover {
  text-decoration: none;
  color: #ffffff;
}
  
/* Chrome, Safari, Edge, Opera */
input::-webkit-outer-spin-button,
input::-webkit-inner-spin-button {
  -webkit-appearance: none;
  margin: 0;
}
  
/* Firefox */
input[type=number] {
  -moz-appearance: textfield;
}

现在,我们已经成功地为我们的mern-stack-app创建了前端。让我们构建后端部分。之前,跳到下一节,看看前端部分如何在没有后端的情况下工作。

运行应用程序的步骤:打开终端并键入以下命令。

npm start

输出:

现在我们将处理应用程序的后端部分。我们将在我们的mern-stack-crud中创建一个文件夹来管理服务器服务,例如数据库、模型、模式、路由和 API,将此文件夹命名为backend

第 1 步:运行命令为服务器创建后端文件夹并进入其中。

mkdir backend && cd backend

第 2 步:创建 package.json –接下来,我们需要创建一个单独的package.json文件来管理我们的mern-stack-crud应用程序的服务器。

npm init -y

转到backend/package.json文件将如下所示。替换测试属性,如:

"test": "echo \"Error: no test specified\" && exit 1" 
"start": "nodemon server.js"

第 3 步:安装节点依赖项 –安装以下节点依赖项。

NPM

Detail

ExpressNode.js framework that helps in creating powerful REST APIs.
body-parserExtracts the entire body portion of a request stream and exposes it on req.body.
corsIt’s a Node.js package that helps in enabling Access-Control-Allow-Origin CORS header.
mongooseIt’s a NoSQL database for creating a robust web application.

要安装上述依赖项,请在终端上运行以下代码。

npm install express body-parser cors mongoose

您可以将nodemon安装为 dev 依赖项以自动执行服务器重新启动过程。

npm i -D nodemon

后端项目结构

后端项目结构

第 4 步:设置 MongoDB 数据库——在这一步中,我们将为我们的应用程序设置一个 MongoDB 数据库。在开始之前,请确保您的系统上安装了最新版本的 MongoDB。在后端文件夹中创建文件夹并将其命名为database 。在数据库文件夹中创建一个名为db.js的文件。转到backend/database/db.js并编写以下代码。

数据库.js

module.exports = {
  db: 'mongodb://localhost:27017/reactdb'
};

我们已经声明了 MongoDB 数据库并将其命名为reactdb

第 5 步:定义Mongoose模式——现在,创建 MongoDB 模式以与 MongoDB 数据库交互。在后端文件夹中创建一个名为models的文件夹来保存与架构相关的文件,并在其中创建一个Student.js文件来定义 MongoDB 架构。转到backend/models/Student.js并编写以下代码。

学生.js

const mongoose = require('mongoose');
const Schema = mongoose.Schema;
  
let studentSchema = new Schema({
  name: {
    type: String
  },
  email: {
    type: String
  },
  rollno: {
    type: Number
  }
}, {
    collection: 'students'
  })
  
module.exports = mongoose.model('Student', studentSchema)

我们在 student Schema 中声明了 name、email 和 rollno 字段以及它们各自的数据类型。

第 6 步:使用 ExpressJS 创建路由——在这一步中,我们使用 Express 和 Node.js 为 CREATE、READ、UPDATE 和 DELETE 设置了一些路由(REST API)。这些路由将帮助我们管理我们的mern-stack-crud应用程序中的数据。创建一个文件夹并将其命名为后端文件夹中的路由。在这里,我们将保留所有与路线相关的文件。此外,在routes文件夹中创建一个文件并将其命名为student.routes.js ,在此文件中我们将定义我们的路线。

mkdir routes && cd routes && touch student.route.js

然后,转到backend/routes/student.route.js文件并编写以下代码。

学生.route.js

let mongoose = require("mongoose"),
  express = require("express"),
  router = express.Router();
  
// Student Model
let studentSchema = require("../models/Student");
  
// CREATE Student
router.post("/create-student", (req, res, next) => {
  studentSchema.create(req.body, (error, data) => {
    if (error) {
      return next(error);
    } else {
      console.log(data);
      res.json(data);
    }
  });
});
  
// READ Students
router.get("/", (req, res) => {
  studentSchema.find((error, data) => {
    if (error) {
      return next(error);
    } else {
      res.json(data);
    }
  });
});
  
// UPDATE student
router
  .route("/update-student/:id")
  // Get Single Student
  .get((req, res) => {
    studentSchema.findById(
        req.params.id, (error, data) => {
      if (error) {
        return next(error);
      } else {
        res.json(data);
      }
    });
  })
  
  // Update Student Data
  .put((req, res, next) => {
    studentSchema.findByIdAndUpdate(
      req.params.id,
      {
        $set: req.body,
      },
      (error, data) => {
        if (error) {
          return next(error);
          console.log(error);
        } else {
          res.json(data);
          console.log("Student updated successfully !");
        }
      }
    );
  });
  
// Delete Student
router.delete("/delete-student/:id", 
(req, res, next) => {
  studentSchema.findByIdAndRemove(
      req.params.id, (error, data) => {
    if (error) {
      return next(error);
    } else {
      res.status(200).json({
        msg: data,
      });
    }
  });
});
  
module.exports = router;

第 7 步:配置 server.js——我们几乎为我们的mern-stack-crud应用程序创建了所有内容。现在,在后端文件夹的根目录中创建server.js文件。转到backend/server.js并编写以下代码。

服务器.js

let express = require('express');
let mongoose = require('mongoose');
let cors = require('cors');
let bodyParser = require('body-parser');
let dbConfig = require('./database/db');
  
// Express Route
const studentRoute = require('../backend/routes/student.route')
  
// Configure mongoDB Database
mongoose.set('useNewUrlParser', true);
mongoose.set('useFindAndModify', false);
mongoose.set('useCreateIndex', true);
mongoose.set('useUnifiedTopology', true);
  
// Connecting MongoDB Database
mongoose.Promise = global.Promise;
mongoose.connect(dbConfig.db).then(() => {
  console.log('Database successfully connected!')
},
  error => {
    console.log('Could not connect to database : ' + error)
  }
)
  
const app = express();
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({
  extended: true
}));
app.use(cors());
app.use('/students', studentRoute)
  
  
// PORT
const port = process.env.PORT || 4000;
const server = app.listen(port, () => {
  console.log('Connected to port ' + port)
})
  
// 404 Error
app.use((req, res, next) => {
  res.status(404).send('Error 404!')
});
  
app.use(function (err, req, res, next) {
  console.error(err.message);
  if (!err.statusCode) err.statusCode = 500;
  res.status(err.statusCode).send(err.message);
});

现在,我们已经成功地为我们的mern-stack-app创建了后端。

我们最终的项目目录结构:

项目目录结构

现在,启动 MongoDB 数据库服务器以运行该服务器。

运行应用程序的步骤:打开终端并运行以下命令,通过留在后端文件夹中来启动 Nodemon 服务器。

npm start

如果一切正常,您将在终端屏幕上看到以下输出。

mern-stack-crud 服务器运行

最终输出:

mern-stack-crud-app