node.js - 当 props.match.params.id 返回 undefined 但 id 在 url 上可见时,如何获取 url 的 id 参数?
问题描述
我第一次使用 MERN 堆栈构建了一个简单的待办事项应用程序,我的快速服务器工作正常,我能够使用邮递员在 mongodb 上执行 CRUD 操作,但是使用反应前端我只能做CR 但无法更新。我还没有编写删除部分的代码。前端带有react router dom,只有3个链接,即通过axios api调用渲染todos的“主页”。用于创建新待办事项的“添加”页面和“编辑”页面,其中通过单击链接按钮将 id 作为道具从待办事项页面传递。
这是我的代码。
快递服务器
const express = require("express");
const todoRoutes = express.Router();
const cors = require("cors");
const path = require("path");
const port = 4000;
const db = require("./db");
db.connect((err)=>{
if(err){
console.log("unable to connect to database");
process.exit(1);
} else {
console.log("connected to the database");
}
})
const app = express();
app.use(cors());
app.use(express.urlencoded({ extended: false }));
app.use(express.json());
/* Get list of Todos */
todoRoutes.route('/').get((req,res)=>{
db.getDB().collection("todos").find({}).toArray((err,docs)=>{
if(err)
console.log(err);
else {
console.log(docs);
res.json(docs);
}
});
});
/* Get Todo */
todoRoutes.route('/:id').get((req,res)=>{
let todoID = req.params.id;
db.getDB().collection("todos").find({_id: db.getPrimaryKey(todoID)}).toArray((err,docs)=>{
if(err)
console.log(err);
else {
console.log(docs);
res.json(docs);
}
});
});
/* Create Todo */
todoRoutes.route('/create').post((req,res)=>{
const userInput = req.body;
db.getDB().collection("todos").insertOne({description:userInput.description,responsible:userInput.responsible,priority:userInput.priority,completed:false},(err,docs)=>{
if(err)
console.log(err);
else{
res.json(docs);
}
});
});
/* Edit todo */
todoRoutes.route('/edit/:id').get((req,res)=>{
let todoID = req.params.id;
db.getDB().collection("todos").find({_id: db.getPrimaryKey(todoID)}).toArray((err,docs)=>{
if(err)
console.log(err);
else {
console.log(docs);
res.json(docs);
}
});
});
todoRoutes.route('/edit/:id').put((req,res)=>{
const todoID = req.params.id;
const userInput = req.body;
db.getDB().collection("todos").updateOne({_id: db.getPrimaryKey(todoID)},{$set:{description:userInput.description,responsible:userInput.responsible,priority:userInput.priority,completed:false}},{returnOrignal:false},(err,docs)=>{
if(err)
console.log(err);
else
res.json(docs)
});
});
/* Delete todo */
todoRoutes.route('/delete/:id').delete((req,res)=>{
const todoID = req.params.id;
db.getDB().collection("todos").deleteOne({_id: db.getPrimaryKey(todoID)},(err,docs)=>{
if(err)
console.log(err)
else{
res.json(docs);
}
});
});
app.use('/',todoRoutes);
app.listen(port,()=>{
console.log(`Server listening to port ${port}`);
});
MongoDB数据库
const MongoClient = require("mongodb").MongoClient;
const ObjectID = require("mongodb").ObjectID;
const url = "actual_url_not_posted_for_security_reasons";
const dbName = "mernstack";
const client = new MongoClient(url,{useNewUrlParser:true});
const state = {
db: null
}
const connect = (cb) =>{ /* callback */
/* if there's a database connection */
if(state.db){
cb();
} else {
client.connect(function(err){
if(err){
cb(err);
} else {
state.db = client.db(dbName);
cb();
}
});
}
}
/* Get the primary key based on the object id */
const getPrimaryKey = (_id)=>{
return ObjectID(_id);
}
/* Get the database */
const getDB = ()=>{
return state.db;
}
module.exports = { getDB, connect, getPrimaryKey};
自定义 REACT Hook 来获取数据
import {useState,useEffect} from 'react';
import axios from 'axios';
const useGetAPI = (url)=>{
const [data,setData] = useState([]);
useEffect(()=>{
const fetchData = async ()=>{
const response = await axios.get(url);
const data = [...response.data];
const error = response.error;
if(error)
console.log(error)
else{
console.log(data);
setData(data);
}
};
fetchData();
},[url])
return data;
}
export default useGetAPI;
TODOS 主页
import React from 'react';
import useGetAPI from '../custom_hooks/useGetAPI';
import Todo from './todo_item/Todo';
const Todos = () =>{
const data = useGetAPI('http://localhost:4000');
return (
<div className="page">
<div className="page-header">
<h1>Todo Lists</h1>
</div>
<div className="page-content">
<ul className="todo-list">
{
data.map((todo)=><Todo todo={todo} key={todo._id}/>)
}
</ul>
</div>
</div>
);
}
export default Todos;
添加待办事项页面
import React,{useState, useEffect, useCallback} from 'react';
import { Redirect } from 'react-router-dom';
import {FaTelegramPlane} from 'react-icons/fa';
import axios from 'axios';
const AddTodo = () =>{
const [description,setDescription] = useState('');
const [responsible,setResponsible] = useState('');
const [priority,setPriority] = useState('');
const [completed,setCompleted] = useState(false);
const [redirect,setRedirect] = useState(false);
const handleDescription = useCallback((e)=>{
setDescription(e.target.value);
},[setDescription]);
const handleResponsible = useCallback((e)=>{
setResponsible(e.target.value);
},[setResponsible]);
const handlePriority = useCallback((e)=>{
setPriority(e.target.value);
},[setPriority]);
const handleSubmit = useCallback((e)=>{
e.preventDefault();
const newTodo = {
description,
responsible,
priority,
completed: false
}
axios.post('http://localhost:3001/create',newTodo)
.then(res=>{
console.log(res.data);
setRedirect(!redirect);
})
.catch(function (error) {
console.log(error);
});
clearInputs();
},[description,responsible,priority,redirect,setRedirect]);
useEffect(()=>{
/* default state of todo */
displayStatus();
return ()=>{
/* after submit state of todo */
displayStatus();
}
});
const clearInputs = ()=>{
/* Clear inputs */
//setTodos([]);
setDescription('');
setResponsible('');
setPriority('');
setCompleted(false);
}
const displayStatus = ()=>{
console.log(`
Description: ${description}
Responsible: ${responsible}
Priority: ${priority}
Completed: ${completed}`);
}
return (
/* If the form submits, the redirect state is updated and redirects to homepage. */
redirect? <Redirect to="/" /> :
<div className="page">
<div className="page-header">
<h1>Create New Todo</h1>
</div>
<div className="page-content">
<form id="add-todo-form" className="todo-form" onSubmit={handleSubmit}>
<div className="form-group">
<label htmlFor="todo_description">Description:</label>
<input id="todo_description" type="text" className="form-control" value={description} onChange={handleDescription} />
</div>
<div className="form-group">
<label htmlFor="todo_responsible">Responsible:</label>
<input id="todo_responsible" type="text" className="form-control" value={responsible} onChange={handleResponsible} />
</div>
<div className="form-group">
<label htmlFor="todo_priorities">Priorities:</label>
<div id="todo_priorities" className="form-radios">
<label htmlFor="radio1" className="radio-label">
<input name="priorityOptions" type="radio" id="radio1" value="Low" checked={priority === 'Low'} onChange={handlePriority}/>
<span className="radiomark"></span>
<span className="radiotext">Low</span>
</label>
<label htmlFor="radio2" className="radio-label">
<input type="radio" id="radio2" value="Medium" checked={priority === 'Medium'} onChange={handlePriority}/>
<span className="radiomark"></span>
<span className="radiotext">Medium</span>
</label>
<label htmlFor="radio3" className="radio-label">
<input type="radio" id="radio3" value="High" checked={priority === 'High'} onChange={handlePriority}/>
<span className="radiomark"></span>
<span className="radiotext">High</span>
</label>
</div>
</div>
<div className="form-group">
<button type="submit" className="form-btn"><FaTelegramPlane />Submit</button>
</div>
</form>
</div>
</div>
);
}
export default AddTodo;
编辑待办事项页面
import React,{useState, useEffect, useContext, useCallback} from 'react';
import useGetApiWithParams from '../custom_hooks/useGetApiWithParams';
import {FaTelegramPlane} from 'react-icons/fa';
import axios from 'axios';
const EditTodo = (props) =>{
const data = useGetApiWithParams('http://localhost:4000/edit',props.match.params.id);
console.log(props.match.params.id);
/* Set default data from database */
const [description,setDescription] = useState('');
const [responsible,setResponsible] = useState('');
const [priority,setPriority] = useState('');
const [completed,setCompleted] = useState(false);
const handleDescription = useCallback((e)=>{
setDescription(e.target.value);
},[setDescription]);
const handleResponsible = useCallback((e)=>{
setResponsible(e.target.value);
},[setResponsible]);
const handlePriority = useCallback((e)=>{
setPriority(e.target.value);
},[setPriority]);
const handleCompleted = useCallback((e)=>{
setCompleted(!completed);
},[completed,setCompleted])
const handleSubmit = useCallback((e)=>{
e.preventDefault();
console.log('Form submitted');
console.log(`Description ${description}`);
console.log(`Description ${responsible}`);
console.log(`Description ${priority}`);
console.log(`Description ${completed}`);
const updatedTodo = {
description,
responsible,
priority,
completed: false
}
axios.put(`http://localhost/4000/edit/${props.match.params.id}`, updatedTodo)
.then(res=>console.log(res.data))
.catch(function (error) {
console.log(error);
});
},[description,responsible,priority,completed,props.match.params.id]);
return (
<div className="page">
<div className="page-header">
<h1>Edit Todo</h1>
</div>
<div className="page-content">
<form id="edit-todo-form" className="todo-form" onSubmit={handleSubmit}>
<div className="form-group">
<label htmlFor="description">Description:</label>
<input id="description" type="text" className="form-control" onChange={handleDescription} value={description}/>
</div>
<div className="form-group">
<label htmlFor="responsible">Responsible:</label>
<input id="responsible" type="text" className="form-control" onChange={handleResponsible} value={responsible}/>
</div>
<div className="form-group">
<label htmlFor="priorities">Priorities:</label>
<div id="priorities" className="form-radios">
<label htmlFor="radio1" className="radio-label">
<input name="priorityOptions" type="radio" id="radio1" value="Low" checked={priority === 'Low'} onChange={handlePriority}/>
<span className="radiomark"></span>
<span className="radiotext">Low</span>
</label>
<label htmlFor="radio2" className="radio-label">
<input type="radio" id="radio2" value="Medium" checked={priority === 'Medium'} onChange={handlePriority}/>
<span className="radiomark"></span>
<span className="radiotext">Medium</span>
</label>
<label htmlFor="radio3" className="radio-label">
<input type="radio" id="radio3" value="High" checked={priority === 'High'} onChange={handlePriority}/>
<span className="radiomark"></span>
<span className="radiotext">High</span>
</label>
</div>
</div>
<div className="form-group">
<label htmlFor="todo_completed">Status:</label>
<div id="todo_completed">
<label htmlFor="checkcompleted" className="check-label">
<input type="checkbox" id="checkcompleted" value={completed} onChange={handleCompleted}/>
<span className="checkmark"></span>
<span className="checktext">Completed</span>
</label>
</div>
</div>
<div className="form-group">
<button type="submit" className="form-btn"><FaTelegramPlane />Save Changes</button>
</div>
</form>
</div>
</div>
);
}
export default EditTodo;
带有 id 参数的自定义 REACT Hook - 由上面的编辑页面使用。
import {useState,useEffect} from 'react';
import axios from 'axios';
const useGetApiWithParams = (url,params)=>{
const [data,setData] = useState([]);
useEffect(()=>{
const fetchData = async ()=>{
const response = await axios.get(`${url}/${params}`);
const data = [...response.data];
const error = response.error;
if(error)
console.log(`Error: ${error}`)
else{
console.log(data);
setData(data);
}
};
fetchData();
},[url,params])
return data;
}
export default useGetApiWithParams;
正如您在编辑页面中看到的那样,表单没有填充数据,而我能够从待办事项页面获取作为链接传递的 id 参数;我无法获取数据,这是 url 与 mongodb 的对象 id 的样子:
我该如何解决这个问题?谢谢!
解决方案
您必须导入这些import { withRouter } from "react-router-dom"
并将这些传入export default withRouter(EditTodo)
;
推荐阅读
- git - Git P4 rebase 因 3 路合并问题而失败
- mysql - 左连接减少返回的行数
- css - 如何将标签文本分别与div分开?
- azure-devops - 如何使用继承过程集合在 Azure DevOps 服务器中查看错误趋势报告
- python - 使用 Dataframe 单元格值设置具有相同尺寸的另一个 Dataframe
- java - 将 Kotlin 翻译成 Java:mapTo
- ansible - regex_findall 中的 with_items 问题
- c# - LINQ to Entities 不支持指定的类型成员“日期”异常 - 在 LINQ 中比较两个日期时间值时,条件
- java - sa认证错误,但是可以用sa账号登录sql server management studio
- python - matplotlib/python 的 Latex 实现问题:RuntimeError: Latex command not found