Я впервые создаю простое приложение todo, использующее стек MERN, мой экспресс-сервер работает нормально, и я могу выполнять операции CRUD на mongodb с почтальоном, однако с интерфейсом реакции я могутолько сделать CR, но не в состоянии обновитьЯ еще не закодировал раздел удаления.Фронт-энд поставляется с реагирующим маршрутизатором dom, имеющим всего 3 ссылки, «домашнюю страницу», на которой задачи отображаются через вызов axios api.Страница «Добавить» для создания новой задачи и страница «Редактирование», где идентификатор передается в качестве реквизита со страницы задачи при нажатии кнопки ссылки.
Вот мой код.
Экспресс-сервер
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};
Custom 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;
ДОБАВИТЬ страницу todo
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;
Изменить страницу todo
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;
Пользовательский REACT Hook с параметром id - используется на странице редактирования выше.
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, переданный в виде ссылки со страницы todo;я не могу получить данные, вот как выглядит URL с идентификатором объекта mongodb:
Как мне решить эту проблему? СПАСИБО!