在使用反应挂钩将新项目添加到数据库后,如何更新反应中的组件?
How can I update component in React after adding a new item into the database, using react hooks?
我想在添加存储到后端的新评论后更新组件,并用具有新评论的新组件更新先前显示的组件。
这是 Page.js 加载单个 post/article 及其评论:
import React ,{useState, useContext, useEffect}from 'react';
import {useHistory,Redirect, useParams} from 'react-router-dom'
import axios from 'axios'
import '../CSS/Page.css'
import CreateComment from './CreateComment';
const Page=()=>{
const [show,setShow]=useState({
commentBox:false
})
const [post,setPost] = useState(
{id:'', username:'', title:'',body:'',date:'',comments:[{}]
})
let {postTitle}=useParams()
useEffect(() => {
axios.get(`http://localhost:2000/apiEndpoint/singlePost/${postTitle}`,{withCredentials:true},{
headers: {
'Content-Type': 'application/json'
}
}).then((res)=>{
console.log(res.data)
const postS=res.data
setPost({...post,id:postS._id, username:postS.username, title:postS.title, body:postS.body,comments: postS.comments})
return;
}).catch((err)=>{
console.log([err])
})
},[]);
const handleCommentButton=(e)=>{
setShow({ ...show, commentBox:!show.commentBox });
}
return (
<div className='postContainer'>
<div className='singlePostcontainer'>
<div className='singlePost' >
<h1>{post.title}</h1>
<hr/>
<p>{post.body} </p>
{post.id}
<hr/>
<h5>Comments:<button className='btnCom' onClick={handleCommentButton}>{show.commentBox?'Close':'Add Comment'}</button></h5>
{show.commentBox?(<CreateComment post={post} />):''}
{post.comments.map(comment=>{
const commentID=comment._id
return(
<div className='comment' key={commentID}>
<h3>{comment.body}</h3>
<h6>By: {comment.creater}</h6>
</div>
)
})}
</div>
</div>
</div>
)
}
export default Page
这是 CreateComment.js 组件,它有一个表单和 post 对数据库的请求:
import React, { Component,useState, useEffect, lazy } from 'react';
import Cookies from 'js-cookie';
import { Link, Redirect } from 'react-router-dom';
const axios = require('axios').default;
const CreateComment=(props)=>{
var commentStr={
body:''
}
const [comment, setComment] = useState(commentStr);
const handleSubmitA=(e)=>{
e.preventDefault()
console.log('This is the id:',props.post.id)
axios.post(`http://localhost:2000/apiEndpoint/CREATE/comment/${props.post.id}`,{
body:comment.body
},
{withCredentials:true},{
headers: {
'Content-Type': 'application/json'
}}).then(res=>{
console.log(res);
})
}
const handleChangeA=(e)=>{
const {name,value}=e.target
setComment({ ...comment, [name]: value });
}
return(
<div className='commentContainer'>
<form onSubmit={handleSubmitA}>
<label>Enter Comment</label>
<textarea name="body" onChange={handleChangeA}></textarea>
<button>Submit</button>
</form>
</div>
)
}
export default CreateComment
我能够成功添加组件,它也在 post 将评论发送到后端数据库。但是如果我按重新加载,它只会在页面上显示。
我从 useEffect 挂钩中删除了空数组。它给了我一个无限循环,但工作正常。然而,这不是一个好的做法,它会占用本地存储资源,我如何在没有无限循环的情况下执行相同的任务?
您可能有一个过时的关闭。 https://dmitripavlutin.com/react-hooks-stale-closures/
试试这个:
setPost(prevState => ({
...prevState,
id: postS._id,
username: postS.username,
title: postS.title,
body: postS.body,
comments: postS.comments
}))
您实际上并没有在 post 发表评论后更新 post。
简单
一个简单的解决方案是让 <CreateComment />
接受一个可以向 parent 发出新评论可用信号的回调。然后 parent 可以决定如何处理该信息。您可以触发 post 的重新获取以获取所有其他评论和状态更新,这些评论和状态更新可能在用户处理他们的评论时已经达到顶峰。
const CreateComment = (props) => {
const onSubmit = (e) => {
e.preventDefault();
console.log('This is the id:', props.post.id)
axios.post(`http://localhost:2000/apiEndpoint/CREATE/comment/${props.post.id}`, {
body: comment.body
}, {
withCredentials: true
}, {
headers: {
'Content-Type': 'application/json'
}
}).then(res => {
props.onCommentCreated();
});
};
};
如果您的 api returns 发表了评论,您可以改为使用 props.onCommentCreated(res.data)
将评论传递给 parent。然后你不需要重新获取,因为 parent 只会将它推入他们的评论状态切片。
喜欢
如果你想变得非常花哨,你可以考虑从组件中完全删除处理 posts/comments 的逻辑,并将其全部粘贴到一个钩子中。这使您的逻辑可重用。它也更容易推理,因为 usePost
钩子有一个非常明确的 api 并且所有状态争论都发生在幕后。我根本没有测试这个,所以如果你走这条路,你将不得不调整它。
const EMPTY_POST = {
id: '',
username: '',
title: '',
body: '',
date: '',
comments: [{}]
}
const getPost = (title) => {
return axios.get(`http://localhost:2000/apiEndpoint/singlePost/${title}`, {
withCredentials: true
}, {
headers: {
'Content-Type': 'application/json'
}
}).then((res) => res.data);
};
const postComment = (id, content) => {
return axios.post(`http://localhost:2000/apiEndpoint/CREATE/comment/${id}`, {
body: content
}, {
withCredentials: true
}, {
headers: {
'Content-Type': 'application/json'
}
})
}
const usePost = () => {
const [post, setPost] = useState(EMPTY_POST);
const {
title
} = useParams();
const findPost = (title) => {
getPost(title)
.then((res) => {
const postS = res.data;
setPost({
...post,
id: postS._id,
username: postS.username,
title: postS.title,
body: postS.body,
comments: postS.comments
})
})
.catch((e) => {
console.log(":(", e);
});
};
useEffect(() => {
findPost(title)
}, []);
const createComment = (id, comment) => {
postComment(id, comment)
.then(() => findPosts(title)); // refetch?
postComment(id, comment)
.then((result) => {
setPost({ ...post, comments: [...post.comments, result]}));
}); // just include it in.
}
return {
post,
createComment
};
}
现在您的 post 逻辑在一个钩子中,您可以像在您的组件中一样使用它。这是显示可能实现的缩写版本。
const Page = () => {
const { post, createComment } = usePost();
return (
...
{show.commentBox && <CreateComment post={post} onSubmit={createComment } />}
...
};
const CreateComment = (props) => {
const { post, onSubmit } = props;
const [comment, setComment] = useState(commentStr);
const handleSubmit = (e) => {
e.preventDefault();
onSubmit(post.id, comment);
};
return (...);
};
备注
顺便说一句,如果组件卸载而挂起的承诺处于活动状态并会触发状态更新,那么在不检查组件安装状态的情况下在承诺中设置状态将在控制台中向您发出警告。
我想在添加存储到后端的新评论后更新组件,并用具有新评论的新组件更新先前显示的组件。
这是 Page.js 加载单个 post/article 及其评论:
import React ,{useState, useContext, useEffect}from 'react';
import {useHistory,Redirect, useParams} from 'react-router-dom'
import axios from 'axios'
import '../CSS/Page.css'
import CreateComment from './CreateComment';
const Page=()=>{
const [show,setShow]=useState({
commentBox:false
})
const [post,setPost] = useState(
{id:'', username:'', title:'',body:'',date:'',comments:[{}]
})
let {postTitle}=useParams()
useEffect(() => {
axios.get(`http://localhost:2000/apiEndpoint/singlePost/${postTitle}`,{withCredentials:true},{
headers: {
'Content-Type': 'application/json'
}
}).then((res)=>{
console.log(res.data)
const postS=res.data
setPost({...post,id:postS._id, username:postS.username, title:postS.title, body:postS.body,comments: postS.comments})
return;
}).catch((err)=>{
console.log([err])
})
},[]);
const handleCommentButton=(e)=>{
setShow({ ...show, commentBox:!show.commentBox });
}
return (
<div className='postContainer'>
<div className='singlePostcontainer'>
<div className='singlePost' >
<h1>{post.title}</h1>
<hr/>
<p>{post.body} </p>
{post.id}
<hr/>
<h5>Comments:<button className='btnCom' onClick={handleCommentButton}>{show.commentBox?'Close':'Add Comment'}</button></h5>
{show.commentBox?(<CreateComment post={post} />):''}
{post.comments.map(comment=>{
const commentID=comment._id
return(
<div className='comment' key={commentID}>
<h3>{comment.body}</h3>
<h6>By: {comment.creater}</h6>
</div>
)
})}
</div>
</div>
</div>
)
}
export default Page
这是 CreateComment.js 组件,它有一个表单和 post 对数据库的请求:
import React, { Component,useState, useEffect, lazy } from 'react';
import Cookies from 'js-cookie';
import { Link, Redirect } from 'react-router-dom';
const axios = require('axios').default;
const CreateComment=(props)=>{
var commentStr={
body:''
}
const [comment, setComment] = useState(commentStr);
const handleSubmitA=(e)=>{
e.preventDefault()
console.log('This is the id:',props.post.id)
axios.post(`http://localhost:2000/apiEndpoint/CREATE/comment/${props.post.id}`,{
body:comment.body
},
{withCredentials:true},{
headers: {
'Content-Type': 'application/json'
}}).then(res=>{
console.log(res);
})
}
const handleChangeA=(e)=>{
const {name,value}=e.target
setComment({ ...comment, [name]: value });
}
return(
<div className='commentContainer'>
<form onSubmit={handleSubmitA}>
<label>Enter Comment</label>
<textarea name="body" onChange={handleChangeA}></textarea>
<button>Submit</button>
</form>
</div>
)
}
export default CreateComment
我能够成功添加组件,它也在 post 将评论发送到后端数据库。但是如果我按重新加载,它只会在页面上显示。
我从 useEffect 挂钩中删除了空数组。它给了我一个无限循环,但工作正常。然而,这不是一个好的做法,它会占用本地存储资源,我如何在没有无限循环的情况下执行相同的任务?
您可能有一个过时的关闭。 https://dmitripavlutin.com/react-hooks-stale-closures/
试试这个:
setPost(prevState => ({
...prevState,
id: postS._id,
username: postS.username,
title: postS.title,
body: postS.body,
comments: postS.comments
}))
您实际上并没有在 post 发表评论后更新 post。
简单
一个简单的解决方案是让 <CreateComment />
接受一个可以向 parent 发出新评论可用信号的回调。然后 parent 可以决定如何处理该信息。您可以触发 post 的重新获取以获取所有其他评论和状态更新,这些评论和状态更新可能在用户处理他们的评论时已经达到顶峰。
const CreateComment = (props) => {
const onSubmit = (e) => {
e.preventDefault();
console.log('This is the id:', props.post.id)
axios.post(`http://localhost:2000/apiEndpoint/CREATE/comment/${props.post.id}`, {
body: comment.body
}, {
withCredentials: true
}, {
headers: {
'Content-Type': 'application/json'
}
}).then(res => {
props.onCommentCreated();
});
};
};
如果您的 api returns 发表了评论,您可以改为使用 props.onCommentCreated(res.data)
将评论传递给 parent。然后你不需要重新获取,因为 parent 只会将它推入他们的评论状态切片。
喜欢
如果你想变得非常花哨,你可以考虑从组件中完全删除处理 posts/comments 的逻辑,并将其全部粘贴到一个钩子中。这使您的逻辑可重用。它也更容易推理,因为 usePost
钩子有一个非常明确的 api 并且所有状态争论都发生在幕后。我根本没有测试这个,所以如果你走这条路,你将不得不调整它。
const EMPTY_POST = {
id: '',
username: '',
title: '',
body: '',
date: '',
comments: [{}]
}
const getPost = (title) => {
return axios.get(`http://localhost:2000/apiEndpoint/singlePost/${title}`, {
withCredentials: true
}, {
headers: {
'Content-Type': 'application/json'
}
}).then((res) => res.data);
};
const postComment = (id, content) => {
return axios.post(`http://localhost:2000/apiEndpoint/CREATE/comment/${id}`, {
body: content
}, {
withCredentials: true
}, {
headers: {
'Content-Type': 'application/json'
}
})
}
const usePost = () => {
const [post, setPost] = useState(EMPTY_POST);
const {
title
} = useParams();
const findPost = (title) => {
getPost(title)
.then((res) => {
const postS = res.data;
setPost({
...post,
id: postS._id,
username: postS.username,
title: postS.title,
body: postS.body,
comments: postS.comments
})
})
.catch((e) => {
console.log(":(", e);
});
};
useEffect(() => {
findPost(title)
}, []);
const createComment = (id, comment) => {
postComment(id, comment)
.then(() => findPosts(title)); // refetch?
postComment(id, comment)
.then((result) => {
setPost({ ...post, comments: [...post.comments, result]}));
}); // just include it in.
}
return {
post,
createComment
};
}
现在您的 post 逻辑在一个钩子中,您可以像在您的组件中一样使用它。这是显示可能实现的缩写版本。
const Page = () => {
const { post, createComment } = usePost();
return (
...
{show.commentBox && <CreateComment post={post} onSubmit={createComment } />}
...
};
const CreateComment = (props) => {
const { post, onSubmit } = props;
const [comment, setComment] = useState(commentStr);
const handleSubmit = (e) => {
e.preventDefault();
onSubmit(post.id, comment);
};
return (...);
};
备注
顺便说一句,如果组件卸载而挂起的承诺处于活动状态并会触发状态更新,那么在不检查组件安装状态的情况下在承诺中设置状态将在控制台中向您发出警告。