【问题标题】:Component not re-rendering after state change in redux在redux中状态更改后组件不重新渲染
【发布时间】:2020-07-12 19:59:20
【问题描述】:

我正在尝试删除帖子,但看起来我每次都必须刷新页面。当我查看 Chrome 中的 React devtools 时,商店也在刷新后更新。我需要了解这背后的原因。

所以,我在路由上有一个 UserPosts 组件,例如 www.abc.com/profile/jimmy/posts。此页面以卡片的形式包含用户的帖子。这些也有一个删除按钮。

UserPosts.js

import React, { Component } from "react"
import { getUserPosts, getCurrentUser } from "../actions/userActions"
import { connect } from "react-redux"
import Cards from "./Cards"

class UserPosts extends Component {
  componentDidMount() {
    const authToken = localStorage.getItem("authToken")
    if (authToken) {
      this.props.dispatch(getCurrentUser(authToken))
      if (this.props && this.props.userId) {
        this.props.dispatch(getUserPosts(this.props.userId))
      } else {
        return null
      }
    }
  }


  render() {
    const { isFetchingUserPosts, userPosts } = this.props
    console.log(userPosts)
    return isFetchingUserPosts ? (
      <p>Fetching....</p>
    ) : (
      <div>
        {userPosts &&
          userPosts.map(post => {
            return <Cards key={post._id} post={post} />
          })}
      </div>
    )
  }
}

const mapStateToPros = state => {
  return {
    isFetchingUserPosts: state.userPosts.isFetchingUserPosts,
    userPosts: state.userPosts.userPosts,
    userId: state.auth.user._id
  }
}

export default connect(mapStateToPros)(UserPosts)

Cards.js

import React, { Component } from "react"
import { connect } from "react-redux"
import { deletePost } from "../actions/userActions"

class Cards extends Component {

  handleDelete = (_id) => {
    this.props.dispatch(deletePost(_id))
  }

  render() {
    const { _id, title, description } = this.props.post
    return (
      <div className="card">  
        <div className="card-content">
          <div className="media">
            <div className="media-left">
              <figure className="image is-48x48">
                <img
                  src="https://bulma.io/images/placeholders/96x96.png"
                  alt="Placeholder image"
                />
              </figure>
            </div>
            <div className="media-content" style={{border: "1px grey"}}>
              <p className="title is-5">{title}</p>
              <p className="content">{description}</p>
              <button className="button is-success">Edit</button>
              <button onClick={() => {this.handleDelete(_id)}} className="button is-success">Delete</button>
            </div>
          </div>
        </div>
      </div>
    )
  }
}

const mapStateToProps = () => {
  return {
    nothing: "nothing"
  }
}

export default connect(mapStateToProps)(Cards)

删除帖子操作

export const deletePost = (id) => {
    return async (dispatch) => {
      dispatch({ type: "DELETING_POST_START" })
      try {
        const deletedPost = await axios.delete(`http://localhost:3000/api/v1/posts/${id}/delete`)
        dispatch({
          type: "DELETING_POST_SUCCESS",
          data: deletedPost
        })
      } catch(error) {
        dispatch({
          type: "DELETING_POST_FAILURE",
          data: { error: "Something went wrong" }
        })
      }
    }
  }

userPosts 缩减器

const initialState = {
  isFetchingUserPosts: null,
  isFetchedUserPosts: null,
  userPosts: [],
  fetchingUserPostsError: null
}

const userPosts = (state = initialState, action) => {
  switch (action.type) {
    case "FETCHING_USER_POSTS_START":
      return {
        ...state,
        isFetchingUserPosts: true,
        fetchingUserPostsError: null
      }
    case "FETCHING_USER_POSTS_SUCCESS":
      return {
        ...state,
        isFetchingUserPosts: false,
        isFetchedUserPosts: true,
        userPosts: action.data,
        fetchingUserPostsError: null
      }
    case "FETCHING_USER_POSTS_ERROR":
      return {
        ...state,
        isFetchingUserPosts: false,
        isFetchedUserPosts: false,
        fetchingUserPostsError: action.data.error
      }
    default:
      return state
  }
}

export default userPosts

后减速器

const initialState = {
  isAddingPost: false,
  postError: null,
  post: {},
  isFetchingPosts: null,
  isFetchedPosts: null,
  fetchingPostsError: null,
  isDeletingPost: false,
  isDeletedPost: false,
  deletingError: null,
  postList: []
}

const post = (state = initialState, action) => {
  switch (action.type) {
    case "ADD_POST_STARTS":
      return { ...state, isAddingPost: true, postError: null }
    case "ADD_POST_SUCCESS":
      return {
        ...state,
        isAddingPost: false,
        postError: null,
        post: action.data
      }
    case "ADD_POST_ERROR":
      return {
        ...state,
        isAddingPost: false,
        postError: action.data.error,
        post: {}
      }
    case "FETCHING_POSTS_START":
      return {
        ...state,
        isFetchingPosts: true,
        isFetchedPosts: false,
        fetchingPostsError: null
      }
    case "FETCHING_POSTS_SUCCESS":
      return {
        ...state,
        isFetchingPosts: false,
        isFetchedPosts: true,
        fetchingPostsError: null,
        postList: action.data.posts
      }
    case "FETCHING_POSTS_ERROR":
      return {
        ...state,
        isFetchingPosts: false,
        isFetchedPosts: false,
        fetchingPostsError: action.data.error
      }
    case "DELETING_POST_START":
      return {
        ...state,
        isDeletingPost: true,
        deletingError: null
      }
    case "DELETING_POST_SUCCESS":
      const filteredPostList = state.postList.filter(
        post => post._id !== action.data._id
      )
      return {
        ...state,
        isDeletingPost: false,
        isDeletedPost: true,
        postList: filteredPostList,
        deletingError: null
      }
    case "DELETING_POST_ERROR":
      return {
        ...state,
        isDeletingPost: false,
        deletingError: action.data.error
      }
    default:
      return state
  }
}

export default post

我只需要知道如何解决这个问题,以及为什么它没有按预期发生。谢谢。

编辑:我还有一个deletePost 控制器。是不是造成了问题?

router.delete("/:id/delete", postsController.deletePost)

 deletePost: async (req, res, next) => {
    try {
      const post = await Post.findByIdAndDelete(req.params.id)
      if (!post) {
        return res.status(200).json({ error: "No post found"})
      }
      return res.status(200).json({ post })
    } catch(error) {
        return res.json({ error })
    }
  }

【问题讨论】:

  • 将console.log() 放在渲染上,看看你是否在删除后得到更新的数组
  • 我在渲染时将console.log(userPosts) 放在UserPosts.js 中,单击删除按钮后它返回相同的数组。

标签: reactjs express redux react-redux redux-thunk


【解决方案1】:

Export 没有 mapdispatchtoprops,试试这样的。对 Post 组件也一样

import React, { Component } from "react"
import { connect } from "react-redux"
import { deletePost } from "../actions/userActions"

class Cards extends Component {

  handleDelete = (_id) => {
    this.props.deletePostAction(_id)
  }

  render() {
    const { _id, title, description } = this.props.post
    return (
      <div className="card">  
        <div className="card-content">
          <div className="media">
            <div className="media-left">
              <figure className="image is-48x48">
                <img
                  src="https://bulma.io/images/placeholders/96x96.png"
                  alt="Placeholder image"
                />
              </figure>
            </div>
            <div className="media-content" style={{border: "1px grey"}}>
              <p className="title is-5">{title}</p>
              <p className="content">{description}</p>
              <button className="button is-success">Edit</button>
              <button onClick={() => {this.handleDelete(_id)}} className="button is-success">Delete</button>
            </div>
          </div>
        </div>
      </div>
    )
  }
}

const mapStateToProps = () => {
  return {
    nothing: "nothing"
  }
}
const mapDispatchToProps = dispatch => ({
    deletePostAction: () => dispatch(deletePost)
});
export default connect(mapStateToProps,mapDispatchToProps)(Cards)

【讨论】:

  • 这有什么需要?
  • 将操作发送到商店?
  • 这就是this.props.dispatch 正在做的事情。
【解决方案2】:

确保您的 API 在您删除项目时返回 id,如果确实如此,请确保它在 _id 中返回。

这里有一些重构,所以它看起来更具可读性:

UserPosts.js

import React, { Component } from "react";
import { getUserPosts, getCurrentUser } from "../actions/userActions";
import { connect } from "react-redux";
import Cards from "./Cards";

class UserPosts extends Component {
  componentDidMount() {
    const authToken = localStorage.getItem("authToken");
    const { getCurrentUser, getUserPosts, userId } = this.props;

    if (authToken) {
      getCurrentUser(authToken);
      userId ? getUserPosts(userId) : null;
    }
  }

  render() {
    const { isFetchingUserPosts, userPosts } = this.props;

    return isFetchingUserPosts ? (
      <p>Fetching....</p>
    ) : (
      <div>
        {userPosts &&
          userPosts.map((post) => {
            return <Cards key={post._id} post={post} />;
          })}
      </div>
    );
  }
}

const mapStateToPros = (state) => {
  return {
    isFetchingUserPosts: state.userPosts.isFetchingUserPosts,
    userPosts: state.userPosts.userPosts,
    userId: state.auth.user._id,
  };
};

const mapDispatchToProps = (dispatch) => {
  return {
    getCurrentUser: (authToken) => {
      dispatch(getCurrentUser(authToken));
    },
    getUserPosts: (userId) => {
      dispatch(getUserPosts(userId));
    },
  };
};

export default connect(mapStateToPros)(UserPosts);

卡片.js

class Cards extends Component {
  handleDelete = (id) => {
    this.props.deletePost(id);
  };

  render() {
    const { _id, title, description } = this.props.post;
    return (
      <div className="card">
        <div className="card-content">
          <div className="media">
            <div className="media-left">
              <figure className="image is-48x48">
                <img
                  src="https://bulma.io/images/placeholders/96x96.png"
                  alt="Placeholder image"
                />
              </figure>
            </div>
            <div className="media-content" style={{ border: "1px grey" }}>
              <p className="title is-5">{title}</p>
              <p className="content">{description}</p>
              <button className="button is-success">Edit</button>
              <button
                onClick={() => {
                  this.handleDelete(_id);
                }}
                className="button is-success"
              >
                Delete
              </button>
            </div>
          </div>
        </div>
      </div>
    );
  }
}

const mapStateToProps = () => {
  return {
    nothing: "nothing",
  };
};

const mapDispatchToProps = (dispatch) => {
  return {
    deletePost: (id) => {
      dispatch(deletePost(id));
    },
  };
};

export default connect(mapStateToProps)(Cards);

删除帖子操作

export const deletePost = (id) => {
  return async (dispatch) => {
    dispatch({ type: "DELETING_POST_START" });
    axios
      .delete(`http://localhost:3000/api/v1/posts/${id}/delete`)
      .then(({ data }) => {
        // as the post is being returned in data.post
        // get that object and pass to data inside dispatch
        const { post } = data;
        dispatch({
          type: "DELETING_POST_SUCCESS",
          data: post,
        });
      })
      .catch((error) => {
        dispatch({
          type: "DELETING_POST_FAILURE",
          data: { error: "Something went wrong" },
        });
      });
  };
};

【讨论】:

  • 我这样做了axios.delete(localhost:3000/api/v1/posts/${id}/delete) .then(data =&gt; console.log(data.post)),它正在返回已删除的帖子。
  • 你能看看我的问题吗?
  • @ubuntu7788 作为它在data.post 中的返回,您现在需要在调度中传递post。检查更新的代码。
  • 还是一样。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-07-08
  • 1970-01-01
  • 2017-03-26
  • 2018-12-12
  • 1970-01-01
相关资源
最近更新 更多