目标:
我想构建一个帖子部分,您可以在其中编辑帖子(无需转到另一个链接)并保存。
这是后端节点:
//route: PATCH api/posts/:id
//note: edit post by id
//access: private
router.patch('/:id', auth, [
check('text', 'Text is required.').not().isEmpty()
], async (req, res)=> {
const errors = validationResult(req)
if (!errors.isEmpty()) {
return res.status(400).json({errors: errors.array()})
}
try {
let post = await Post.findById(req.params.id)
if (!post) {
return res.status(400).json({msg: 'Post not found'})
}
//check whether logged in user has right to the post
if (post.user.toString() !== req.user.id) {
return res.status(401).json({msg: 'User not authorized'})
}
//check text of the post and save
post.text = req.body.text
post.save()
res.json(post)
} catch (error) {
console.error(error.message)
if (error.kind === 'ObjectId'){
return res.status(400).json({msg: 'Post not found'})
}
res.status(500).send('Server error')
}
})
然后我用Postman测试了它,并且效果很好。 这是Redux Action
//edit post by id
export const editPost = (id, formData) => async dispatch => {
const config = {
headers: {
'Content-Type': 'application/json'
}
}
try {
const res = await axios.patch(`/api/posts/${id}`, formData, config )
dispatch({
type: EDIT_POST,
payload: res.data
})
dispatch(setAlert('Post Updated', 'success'))
} catch (error) {
dispatch({
type: POST_ERROR,
payload: { msg: error.response.statusText, status: error.response.status }
})
}
}
和减速器:
case EDIT_POST:
return {
...state,
posts: [payload, ...state.posts],
loading: false
}
然后是React前端部分:
import React, { useState, Fragment } from 'react'
import PropTypes from 'prop-types'
import { Link } from 'react-router-dom'
import { connect } from 'react-redux'
import Moment from 'react-moment'
import Alert from '../layout/Alert'
import { addLike, removeLike, editPost, deletePost } from '../../actions/post'
const PostItem = ({
auth,
addLike,
removeLike,
editPost,
deletePost,
post : {
_id,
text,
name,
avatar,
user,
likes,
comments,
date
},
showActions,
}) => {
const [liked, toggleLiked ] = useState(false)
const [editMode, toggleEditMode ] = useState(false)
const [postContent, setPostContent ] = useState(text)
const likeButton = () => {
toggleLiked(!liked)
if (!liked) {
addLike(_id)
} else {
removeLike(_id)
}
}
const editPostButton = () => {
toggleEditMode(!editMode)
}
const onSubmit = (e) => {
e.preventDefault()
editPost(_id, {postContent})
// history.push('/api/posts')
}
return (
<div className="post-item">
<div className="post-item__bio">
<img src={avatar} alt=""/>
<h6>{name}</h6>
</div>
<div>
{ editMode ? (
<Fragment key={_id}>
<Alert />
<form className="form form--edit" onSubmit={onSubmit}>
<textarea
name="text"
cols="30"
rows="5"
placeholder="Edit your post"
value={postContent}
onChange={e => setPostContent(e.target.value)}
required
></textarea>
<input type="submit" className="btn" value="Done!" />
</form>
</Fragment>
):(<p>{postContent}</p>)}
<div className="post-item__meta">
<p className="post-item__meta-item">
<Moment format="YYYY/MM/DD">{date}</Moment>
</p>
{showActions && <Fragment>
{ likes.filter(like => like.user === auth.user._id).length > 0 ? (<div className={ "heart red" } onClick={()=> likeButton()} ></div>) : (<div className={ liked ? "heart is-active": "heart"} onClick={()=> likeButton()}></div>) }
<button type="button" onClick={()=> addLike(_id)} className="post-item__meta-item">
<span>{likes.length > 0 && (
<span className='comment-count'>{likes.length} ♥</span>
)}</span>
</button>
<Link to={`/echelon/posts/${_id}`} className="post-item__meta-item">
View {comments.length > 0 && (
<span className='comment-count'>{comments.length}</span>
)} Comments
</Link>
{!auth.loading && user === auth.user._id && (
<Fragment>
<button type="button" onClick={() => editPostButton()} className="post-item__meta-item">
Edit
</button>
<button type="button" onClick={() => deletePost(_id)} className="post-item__meta-item delete">
×
</button>
</Fragment>
)}
</Fragment>}
</div>
</div>
</div>
)
}
PostItem.defaultProps ={
showActions: true
}
PostItem.propTypes = {
post: PropTypes.object.isRequired,
auth: PropTypes.object.isRequired,
addLike: PropTypes.func.isRequired,
removeLike: PropTypes.func.isRequired,
editPost: PropTypes.func.isRequired,
deletePost: PropTypes.func.isRequired,
}
const mapStateToProps = state => ({
auth: state.auth
})
export default connect(mapStateToProps, { addLike, removeLike, editPost, deletePost } )(PostItem)
问题:
在编辑模式下按下提交按钮后,我收到带有发布链接的PATCH 400错误请求。其他所有按钮或链接都可以正常工作。完成(编辑>完成!通过提交)是唯一不起作用的方法。
我对React和Redux还是很陌生,我不确定是哪一步导致了问题。我猜也许是这样,我们没有通过req.params找到ID?那我该如何正确传递ID进行更新呢?
答案 0 :(得分:0)
您的onSubmit
对我来说很奇怪。 editPost
创建一个动作/重击动作,但是该动作不会被调度。此外,history.push
应该浏览页面,所以/api
似乎是错误的。
这可能还会导致您提出错误的请求,但是我不确定,因为history.push
不是标准的全局函数。这取决于它的实现。
答案 1 :(得分:0)
嗨,我现在已经解决了这个问题。
在React代码中:让我们添加
const formData = {
text: postContent
}
const onSubmit = (e) => {
e.preventDefault()
editPost(_id, formData)
toggleEditMode(!editMode)
}
withRouter和历史记录不再是必需的。发生问题是因为我对国家的命名感到困惑。由于我们需要将数据保存到post.text(如您在后端中所见),因此formData必须为{text:data}。在我的原始代码中,它显示为{postContent:data},这就是为什么它不起作用的原因。
为了使所有内容保持最新,我们也需要更改后端,在保存帖子后,我们需要找到所有帖子并重新渲染。
post.text = req.body.text
await post.save()
const posts = await Post.find().sort({date: -1})
res.json(posts)
然后我们还需要更换异径管:
case EDIT_POST:
return {
...state,
posts: payload,
loading: false
}