MERN栈教程中Create按钮无响应及Failed to fetch问题求助
问题描述
按照MongoDB官方MERN栈简易记录列表教程完成所有步骤,已确认数据库连接正常,但测试时出现以下问题:
- 点击
Create Person按钮创建新记录无反应; - 刷新创建页面弹出
Failed to fetch错误提示; - 刷新后再次点击
Create Person按钮,不再弹出提示框,且按钮无法跳转到首页; - 记录列表无法显示数据。
前端 create.js 代码
// /client/src/components/create.js import React, { useState } from "react"; import { useNavigate } from "react-router"; export default function Create() { const [form, setForm] = useState({ name: "", position: "", level: "", }); const navigate = useNavigate(); // 更新表单状态 function updateForm(value) { return setForm((prev) => { return { ...prev, ...value }; }); } // 表单提交处理 async function onSubmit(e) { e.preventDefault(); const newPerson = { ...form }; await fetch("http://localhost:5000/record/add", { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify(newPerson), }) .catch(error => { console.log(error); return; }); setForm({ name: "", position: "", level: "" }); navigate("/"); } // 渲染表单 return ( <div> <h3>Create New Record</h3> <form onSubmit={onSubmit}> <div className="form-group"> <label htmlFor="name">Name</label> <input type="text" className="form-control" id="name" value={form.name} onChange={(e) => updateForm({ name: e.target.value })} /> </div> <div className="form-group"> <label htmlFor="position">Position</label> <input type="text" className="form-control" id="position" value={form.position} onChange={(e) => updateForm({ position: e.target.value })} /> </div> <div className="form-group"> <div className="form-check form-check-inline"> <input className="form-check-input" type="radio" name="positionOptions" id="positionIntern" value="Intern" checked={form.level === "Intern"} onChange={(e) => updateForm({ level: e.target.value })} /> <label htmlFor="positionIntern" className="form-check-label">Intern</label> </div> <div className="form-check form-check-inline"> <input className="form-check-input" type="radio" name="positionOptions" id="positionJunior" value="Junior" checked={form.level === "Junior"} onChange={(e) => updateForm({ level: e.target.value })} /> <label htmlFor="positionJunior" className="form-check-label">Junior</label> </div> <div className="form-check form-check-inline"> <input className="form-check-input" type="radio" name="positionOptions" id="positionSenior" value="Senior" checked={form.level === "Senior"} onChange={(e) => updateForm({ level: e.target.value })} /> <label htmlFor="positionSenior" className="form-check-label">Senior</label> </div> </div> <div className="form-group"> <input type="submit" value="Create person" className="btn btn-primary" /> </div> </form> </div> ); }
前端 recordList.js 代码
// /client/src/components/recordList.js import React, { useEffect, useState } from "react"; import { Link } from "react-router-dom"; const Record = (props) => ( <tr> <td>{props.record.name}</td> <td>{props.record.position}</td> <td>{props.record.level}</td> <td> <Link className="btn btn-link" to={`/edit/${props.record._id}`}>Edit</Link> | <button className="btn btn-link" onClick={() => { props.deleteRecord(props.record._id); }} > Delete </button> </td> </tr> ); export default function RecordList() { const [records, setRecords] = useState([]); // 获取记录数据 useEffect(() => { async function getRecords() { const response = await fetch(`http://localhost:5000/record/`); if (!response.ok) { const message = `An error occurred: ${response.statusText}`; window.alert(message); return; } const records = await response.json(); setRecords(records); } getRecords(); return; }, [records.length]); // 删除记录 async function deleteRecord(id) { await fetch(`http://localhost:5000/${id}`, { method: "DELETE" }); const newRecords = records.filter((el) => el._id !== id); setRecords(newRecords); } // 渲染记录列表 function recordList() { return records.map((record) => { return ( <Record record={record} deleteRecord={() => deleteRecord(record._id)} key={record._id} /> ); }); } // 渲染页面 return ( <div> <h3>Record List</h3> <table className="table table-striped" style={{ marginTop: 20 }}> <thead> <tr> <th>Name</th> <th>Position</th> <th>Level</th> <th>Action</th> </tr> </thead> <tbody>{recordList()}</tbody> </table> </div> ); }
后端 record.js 代码
const express = require("express"); // 定义记录路由 const recordRoutes = express.Router(); // 数据库连接工具 const dbo = require("../db/conn"); // ObjectId转换工具 const ObjectId = require("mongodb").ObjectId; // 获取所有记录 recordRoutes.route("/record").get(function (req, res) { let db_connect = dbo.getDb("todolist"); db_connect .collection("records") .find({}) .toArray(function (err, result) { if (err) throw err; res.json(result); }); }); // 根据ID获取单条记录 recordRoutes.route("/record/:id").get(function (req, res) { let db_connect = dbo.getDb("todolist"); let myquery = { _id: ObjectId(req.params.id) }; db_connect .collection("records") .findOne(myquery, function (err, result) { if (err) throw err; res.json(result); }); }); // 创建新记录 recordRoutes.route("/record/add").post(function (req, response) { let db_connect = dbo.getDb("todolist"); let myobj = { name: req.body.name, position: req.body.position, level: req.body.level, }; db_connect.collection("records").insertOne(myobj, function (err, res) { if (err) throw err; response.json(res); }); }); // 更新记录 recordRoutes.route("/update/:id").post(function (req, response) { let db_connect = dbo.getDb("todolist"); let myquery = { _id: ObjectId(req.params.id) }; let newvalues = { $set: { name: req.body.name, position: req.body.position, level: req.body.level, }, }; db_connect .collection("records") .updateOne(myquery, newvalues, function (err, res) { if (err) throw err; console.log("1 document updated"); response.json(res); }); }); // 删除记录 recordRoutes.route("/:id").delete((req, response) => { let db_connect = dbo.getDb("todolist"); let myquery = { _id: ObjectId(req.params.id) }; db_connect.collection("records").deleteOne(myquery, function (err, obj) { if (err) throw err; console.log("1 document deleted"); response.json(obj); }); }); module.exports = recordRoutes;
问题排查与修复方案
1. 跨域资源共享(CORS)问题
前端默认运行在3000端口,后端在5000端口,浏览器会拦截跨域请求,导致Failed to fetch错误。
修复:
- 后端安装cors包:
npm install cors - 在后端主入口文件(如server.js)中添加:
const cors = require('cors'); app.use(cors());
2. Delete请求路径错误
recordList.js中delete请求路径为http://localhost:5000/${id},但后端delete路由/:id挂载在/record路由下,实际有效路径应为/record/${id}。
修复:
修改recordList.js中的delete请求:
await fetch(`http://localhost:5000/record/${id}`, { method: "DELETE" });
3. useEffect依赖循环问题
recordList.js中useEffect依赖records.length,每次删除记录后长度变化会触发重复请求,导致不必要的资源消耗。
修复:
将useEffect依赖改为空数组,仅在组件挂载时请求一次数据:
useEffect(() => { // 原有getRecords代码 }, []);
4. 错误处理逻辑缺陷
create.js中fetch的catch仅打印错误,未阻止后续的navigate操作,导致请求失败时仍会跳转到首页。
修复:
修改onSubmit函数,仅在请求成功后重置表单并跳转:
async function onSubmit(e) { e.preventDefault(); const newPerson = { ...form }; try { const response = await fetch("http://localhost:5000/record/add", { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify(newPerson), }); if (!response.ok) { throw new Error('创建请求失败'); } setForm({ name: "", position: "", level: "" }); navigate("/"); } catch (error) { console.error(error); alert('创建记录失败,请检查输入或连接状态'); } }
内容的提问来源于stack exchange,提问作者Hoko L
相关产品推荐
相关产品推荐

