在 React 应用程序中设置正确的 MongoDB 实现
Setting up proper MongoDB implementation in React app
所以,目前我正在实习 React (MERN) 应用程序,这是一个简单的待办事项列表,能够创建、删除和编辑待办事项。我将从中 post 一些代码,但您也可以在 GitHub 上查看完整代码:https://github.com/Wonderio619/magisale-internship-todo
下一个任务是将我的应用程序连接到 MongoDB。我有一些 "boilerplate" 代码——我已经建立了与 MongoDB 的连接,也有 Express 路由器,其中包含获取所有待办事项列表、将待办事项发送到数据库、使用 ID 更新待办事项、使用 ID 获取待办事项等路由:
const express = require("express");
const router = express.Router();
let Todo = require('../models/model')
// get all todo list with id
router.get('/', function (req, res) {
Todo.find()
.then((todos) => res.json(todos))
.catch((error) => res.send(error))
})
// send todo to database
router.post('/', function (req, res) {
let todo = new Todo();
todo.titleText = req.body.title;
todo.todoText = req.body.body;
todo.save(function (err) {
if (err)
res.send(err);
res.send('Todo successfully added!');
});
})
// get todo with id
router.get('/:todoId', function (req, res) {
Todo.findById(req.params.todoId)
.then(foundTodo => res.json(foundTodo))
.catch(error => res.send(error));
})
// updates todo with id
router.put('/:todoId', function (req, res) {
Todo.findOneAndUpdate({ _id: req.params.todoId }, req.body, { new: true })
.then((todo) => res.json(todo))
.catch((error) => res.send(error))
})
// deletes todo with id
router.delete('/:todoId', function (req, res) {
Todo.remove({ _id: req.params.todoId })
.then(() => res.json({ message: 'todo is deleted' }))
.catch((error) => res.send(error))
})
module.exports = router;
调用待办应用相应方法时使用的路由:
import React, { Component } from 'react';
import './ToDo.css';
import Logo from './assets/logo.png';
import ToDoItem from './components/ToDoItem';
import AppBar from './components/AppBar';
import Popover from './components/Popover';
import { connect } from 'react-redux';
class ToDo extends Component {
constructor(props) {
super(props);
this.state = {
list: [],
title: '',
todo: '',
};
};
componentDidMount = () => {
fetch("/api/todos")
.then(data => data.json())
.then(res => this.setState({ list: res.data }));
console.log(this.state.list)
};
createNewToDoItem = () => {
fetch("/api/todos", {
method: "post",
headers: new Headers({
"Content-Type": "application/json"
}),
body: JSON.stringify({
title: this.state.title,
body: this.state.todo
})
})
.catch(err => {
console.error(err);
});
if (this.state.title !== '' & this.state.todo !== '') {
this.props.createTodoItem(this.state.title, this.state.todo);
this.setState({ title: '', todo: '' });
}
};
handleTitleInput = e => {
this.setState({
title: e.target.value,
});
};
handleTodoInput = e => {
this.setState({
todo: e.target.value,
});
};
editItem = (i, updTitle, updToDo) => {
const modifyURL = "/api/todos/" + i;
fetch(modifyURL, {
method: "put",
headers: new Headers({
"Content-Type": "application/json"
}),
body: JSON.stringify({
title: updTitle,
todo: updToDo
})
})
.then(resp => {
if (!resp.ok) {
if (resp.status >= 400 && resp.status < 500) {
return resp.json().then(data => {
let error = { errorMessage: data.message };
throw error;
});
} else {
let error = {
errorMessage: "Please try again later. Server is not online"
};
throw error;
}
}
return resp.json();
})
.then(newTodo => {
let arr = this.props.list;
arr[i].title = updTitle;
arr[i].todo = updToDo;
this.setState({ updateList: true });
});
};
deleteItem = indexToDelete => {
const deleteURL = "/api/todos/" + indexToDelete;
fetch(deleteURL, {
method: "delete"
})
.then(resp => {
if (!resp.ok) {
if (resp.status >= 400 && resp.status < 500) {
return resp.json().then(data => {
let error = { errorMessage: data.message };
throw error;
});
} else {
let error = {
errorMessage: "Please try again later. Server is not online"
};
throw error;
}
}
return resp.json();
})
.then(() => {
this.props.deleteTodoItem(indexToDelete);
});
};
randId() {
return Math.random().toString(36).replace(/[^a-z]+/g, '').substr(2, 10);
}
eachToDo = (item, i) => {
return <ToDoItem
key={this.randId()}
title={item.title}
todo={item.todo}
deleteItem={this.deleteItem.bind(this, i)}
editItem={this.editItem.bind(this, i)}
/>
};
render() {
const { list } = this.props;
return (
<div className="ToDo">
<img className="Logo" src={Logo} alt="React logo" />
<AppBar />
<div className="ToDo-Container">
<div className="ToDo-Content">
{list.map(this.eachToDo)}
</div>
<div>
<Popover
toDoValue={this.state.todo}
titleValue={this.state.title}
titleOnChange={this.handleTitleInput}
toDoOnChange={this.handleTodoInput}
addHandler={this.createNewToDoItem}
/>
</div>
</div>
</div>
);
}
}
const mapStateToProps = (state) => {
return {
list: state.list
}
}
const mapDispatchToProps = dispatch => {
return {
deleteTodoItem: id => {
dispatch({ type: "DELETE_TODO", id: id });
},
createTodoItem: (title, todo) => {
dispatch({ type: "CREATE_TODO", title: title, todo: todo });
}
};
};
export default connect(mapStateToProps, mapDispatchToProps)(ToDo);
请注意,"list" 状态数组并没有真正使用,因为我在这里的 Redux 状态中有初始列表状态(它可能实现不好,但无论如何):
const initState = {
list: [
{
title: 'Cup cleaning',
todo: "Wash and take away the Kurzhiy's cup from WC"
},
{
title: 'Smoking rollton',
todo: 'Do some rollton and cigarettes'
},
{
title: 'Curious dream',
todo: 'Build a time machine'
}
],
};
const rootReducer = (state = initState, action) => {
switch (action.type) {
case "DELETE_TODO":
let newList = state.list.filter((todo, index) => action.id !== index)
return {
...state,
list: newList
}
case "CREATE_TODO":
const title = action.title;
const todo = action.todo;
let createdList = [
...state.list,
{
title,
todo
}
]
return {
...state,
list: createdList
}
default:
return state;
}
}
export default rootReducer;
所以,现在我需要一些帮助 - 如果我理解正确,我的列表状态现在应该存储在 MongoDB 数据库中。但目前它在 Redux 中,我应该如何从当前状态实现正确切换到 MongoDB?
另外我明白我的 MongoDB 实现远非完美,我只是新手,但我需要解决以下问题:
1) 我试图在 ComponentDidMount 方法中从数据库中获取所有待办事项并将其保存在数组中,但是 console.log 总是显示数组为空 smth 那里肯定是错误的。
2)还没有真正建立与数据库的连接,因为通常我只能将待办事项添加到数据库,但删除或编辑功能不起作用,因为我对如何实现这个索引的东西有点困惑,我应该使用 ObjectId 属性 从 MongoDB 还是我应该将索引从我的主要组件传递到数据库,以及如何传递?
此外,任何关于正确 mongodb 实施的全球建议以及对我的代码的建议或修复将不胜感激 :)
不是res.data而是你应该在你的状态下注入。 res.data 未定义,因此不会更新 state.list.
componentDidMount = () => {
fetch("/api/todos")
.then(data => data.json())
.then(jsonData => {
console.log('jsonData --', jsonData)
console.log('jsonData.data is empty!', jsonData.data)
this.setState({ list: jsonData })
});
};
1- 为了能够更新,您需要发送一个 ID。如果这是您想要查找待办事项的方式,您可以在数据库中创建 ID。
请注意 _id
不同于 id
。
_id
mongodb的ObjectId,不是整数类型,而是ObjectId类型。
id
只是您创建的一个名为 id 的常规字段。
注意:您的 req.params.todoId
是一个整数。而 ObjectId 是 ObjectId 类型!所以你将无法查询类型错误的。
var todoSchema = new Schema({
id: Number,
titleText: String,
todoText: String
});
2- 获取您的待办事项并根据 ID 进行更新。如果它不存在,它将通过 upsert 选项创建。不要忘记投射以匹配您的模式。 title: req.body.title
将不起作用,因为您在架构中将其定义为 titleText
。
// updates todo with id
router.put('/:todoId', function (req, res) {
const data = {
titleText: req.body.title,
todoText: req.body.todo
}
Todo.findOneAndUpdate(
{ id: req.params.todoId }, // the query
{ $set: data }, // things to update
{ upsert: true } // upsert option
).then((todo) => res.json(todo))
.catch((error) => res.send(error))
})
所以,目前我正在实习 React (MERN) 应用程序,这是一个简单的待办事项列表,能够创建、删除和编辑待办事项。我将从中 post 一些代码,但您也可以在 GitHub 上查看完整代码:https://github.com/Wonderio619/magisale-internship-todo
下一个任务是将我的应用程序连接到 MongoDB。我有一些 "boilerplate" 代码——我已经建立了与 MongoDB 的连接,也有 Express 路由器,其中包含获取所有待办事项列表、将待办事项发送到数据库、使用 ID 更新待办事项、使用 ID 获取待办事项等路由:
const express = require("express");
const router = express.Router();
let Todo = require('../models/model')
// get all todo list with id
router.get('/', function (req, res) {
Todo.find()
.then((todos) => res.json(todos))
.catch((error) => res.send(error))
})
// send todo to database
router.post('/', function (req, res) {
let todo = new Todo();
todo.titleText = req.body.title;
todo.todoText = req.body.body;
todo.save(function (err) {
if (err)
res.send(err);
res.send('Todo successfully added!');
});
})
// get todo with id
router.get('/:todoId', function (req, res) {
Todo.findById(req.params.todoId)
.then(foundTodo => res.json(foundTodo))
.catch(error => res.send(error));
})
// updates todo with id
router.put('/:todoId', function (req, res) {
Todo.findOneAndUpdate({ _id: req.params.todoId }, req.body, { new: true })
.then((todo) => res.json(todo))
.catch((error) => res.send(error))
})
// deletes todo with id
router.delete('/:todoId', function (req, res) {
Todo.remove({ _id: req.params.todoId })
.then(() => res.json({ message: 'todo is deleted' }))
.catch((error) => res.send(error))
})
module.exports = router;
调用待办应用相应方法时使用的路由:
import React, { Component } from 'react';
import './ToDo.css';
import Logo from './assets/logo.png';
import ToDoItem from './components/ToDoItem';
import AppBar from './components/AppBar';
import Popover from './components/Popover';
import { connect } from 'react-redux';
class ToDo extends Component {
constructor(props) {
super(props);
this.state = {
list: [],
title: '',
todo: '',
};
};
componentDidMount = () => {
fetch("/api/todos")
.then(data => data.json())
.then(res => this.setState({ list: res.data }));
console.log(this.state.list)
};
createNewToDoItem = () => {
fetch("/api/todos", {
method: "post",
headers: new Headers({
"Content-Type": "application/json"
}),
body: JSON.stringify({
title: this.state.title,
body: this.state.todo
})
})
.catch(err => {
console.error(err);
});
if (this.state.title !== '' & this.state.todo !== '') {
this.props.createTodoItem(this.state.title, this.state.todo);
this.setState({ title: '', todo: '' });
}
};
handleTitleInput = e => {
this.setState({
title: e.target.value,
});
};
handleTodoInput = e => {
this.setState({
todo: e.target.value,
});
};
editItem = (i, updTitle, updToDo) => {
const modifyURL = "/api/todos/" + i;
fetch(modifyURL, {
method: "put",
headers: new Headers({
"Content-Type": "application/json"
}),
body: JSON.stringify({
title: updTitle,
todo: updToDo
})
})
.then(resp => {
if (!resp.ok) {
if (resp.status >= 400 && resp.status < 500) {
return resp.json().then(data => {
let error = { errorMessage: data.message };
throw error;
});
} else {
let error = {
errorMessage: "Please try again later. Server is not online"
};
throw error;
}
}
return resp.json();
})
.then(newTodo => {
let arr = this.props.list;
arr[i].title = updTitle;
arr[i].todo = updToDo;
this.setState({ updateList: true });
});
};
deleteItem = indexToDelete => {
const deleteURL = "/api/todos/" + indexToDelete;
fetch(deleteURL, {
method: "delete"
})
.then(resp => {
if (!resp.ok) {
if (resp.status >= 400 && resp.status < 500) {
return resp.json().then(data => {
let error = { errorMessage: data.message };
throw error;
});
} else {
let error = {
errorMessage: "Please try again later. Server is not online"
};
throw error;
}
}
return resp.json();
})
.then(() => {
this.props.deleteTodoItem(indexToDelete);
});
};
randId() {
return Math.random().toString(36).replace(/[^a-z]+/g, '').substr(2, 10);
}
eachToDo = (item, i) => {
return <ToDoItem
key={this.randId()}
title={item.title}
todo={item.todo}
deleteItem={this.deleteItem.bind(this, i)}
editItem={this.editItem.bind(this, i)}
/>
};
render() {
const { list } = this.props;
return (
<div className="ToDo">
<img className="Logo" src={Logo} alt="React logo" />
<AppBar />
<div className="ToDo-Container">
<div className="ToDo-Content">
{list.map(this.eachToDo)}
</div>
<div>
<Popover
toDoValue={this.state.todo}
titleValue={this.state.title}
titleOnChange={this.handleTitleInput}
toDoOnChange={this.handleTodoInput}
addHandler={this.createNewToDoItem}
/>
</div>
</div>
</div>
);
}
}
const mapStateToProps = (state) => {
return {
list: state.list
}
}
const mapDispatchToProps = dispatch => {
return {
deleteTodoItem: id => {
dispatch({ type: "DELETE_TODO", id: id });
},
createTodoItem: (title, todo) => {
dispatch({ type: "CREATE_TODO", title: title, todo: todo });
}
};
};
export default connect(mapStateToProps, mapDispatchToProps)(ToDo);
请注意,"list" 状态数组并没有真正使用,因为我在这里的 Redux 状态中有初始列表状态(它可能实现不好,但无论如何):
const initState = {
list: [
{
title: 'Cup cleaning',
todo: "Wash and take away the Kurzhiy's cup from WC"
},
{
title: 'Smoking rollton',
todo: 'Do some rollton and cigarettes'
},
{
title: 'Curious dream',
todo: 'Build a time machine'
}
],
};
const rootReducer = (state = initState, action) => {
switch (action.type) {
case "DELETE_TODO":
let newList = state.list.filter((todo, index) => action.id !== index)
return {
...state,
list: newList
}
case "CREATE_TODO":
const title = action.title;
const todo = action.todo;
let createdList = [
...state.list,
{
title,
todo
}
]
return {
...state,
list: createdList
}
default:
return state;
}
}
export default rootReducer;
所以,现在我需要一些帮助 - 如果我理解正确,我的列表状态现在应该存储在 MongoDB 数据库中。但目前它在 Redux 中,我应该如何从当前状态实现正确切换到 MongoDB?
另外我明白我的 MongoDB 实现远非完美,我只是新手,但我需要解决以下问题: 1) 我试图在 ComponentDidMount 方法中从数据库中获取所有待办事项并将其保存在数组中,但是 console.log 总是显示数组为空 smth 那里肯定是错误的。 2)还没有真正建立与数据库的连接,因为通常我只能将待办事项添加到数据库,但删除或编辑功能不起作用,因为我对如何实现这个索引的东西有点困惑,我应该使用 ObjectId 属性 从 MongoDB 还是我应该将索引从我的主要组件传递到数据库,以及如何传递?
此外,任何关于正确 mongodb 实施的全球建议以及对我的代码的建议或修复将不胜感激 :)
不是res.data而是你应该在你的状态下注入。 res.data 未定义,因此不会更新 state.list.
componentDidMount = () => {
fetch("/api/todos")
.then(data => data.json())
.then(jsonData => {
console.log('jsonData --', jsonData)
console.log('jsonData.data is empty!', jsonData.data)
this.setState({ list: jsonData })
});
};
1- 为了能够更新,您需要发送一个 ID。如果这是您想要查找待办事项的方式,您可以在数据库中创建 ID。
请注意 _id
不同于 id
。
_id
mongodb的ObjectId,不是整数类型,而是ObjectId类型。
id
只是您创建的一个名为 id 的常规字段。
注意:您的 req.params.todoId
是一个整数。而 ObjectId 是 ObjectId 类型!所以你将无法查询类型错误的。
var todoSchema = new Schema({
id: Number,
titleText: String,
todoText: String
});
2- 获取您的待办事项并根据 ID 进行更新。如果它不存在,它将通过 upsert 选项创建。不要忘记投射以匹配您的模式。 title: req.body.title
将不起作用,因为您在架构中将其定义为 titleText
。
// updates todo with id
router.put('/:todoId', function (req, res) {
const data = {
titleText: req.body.title,
todoText: req.body.todo
}
Todo.findOneAndUpdate(
{ id: req.params.todoId }, // the query
{ $set: data }, // things to update
{ upsert: true } // upsert option
).then((todo) => res.json(todo))
.catch((error) => res.send(error))
})