React + Redux前端无法与Rest后端通信

时间:2019-08-30 14:27:28

标签: node.js reactjs redux axios

目标:

我想构建一个帖子部分,您可以在其中编辑帖子(无需转到另一个链接)并保存。

这是后端节点:

//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} &#x2665;</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">
                            &times;
                            </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进行更新呢?

2 个答案:

答案 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
      }