Danial Ahmed
Danial Ahmed

Reputation: 866

Redux react not re-rendering component after state changes

I have posts and each post has comments, when I am adding a new comment the change is displayed (rendered) until I refresh the page manually but I can see in redux-dev tools that ADD_COMMENT action is being called and state of post is changing. What I want is to show the comment on page as soon as it is added to database. (State change and rendering works fine on Post Add,Delete,Update). DIFF

STATE

posts.js (contains all the posts):

class Posts extends Component {
  static propTypes = {
    posts: PropTypes.array.isRequired,
    getPosts: PropTypes.func.isRequired,
    deletePost: PropTypes.func.isRequired,
  };
  componentDidMount() {
    this.props.getPosts();
  }
  onDelete = (id) => {
    this.props.deletePost(id);
  };
  render() {
    return (
      <React.Fragment>
        {this.props.posts.map((_p) => (
          <Post _p={_p} onDelete={this.onDelete} key={_p.id} />
        ))}
      </React.Fragment>
    );
  }
}
const mapToPropTypes = (state) => {
  return { posts: state.posts.posts };
};
export default connect(mapToPropTypes, { getPosts, deletePost })(Posts);

post.js (Single post):

class Post extends Component {
  handleDelete = (id) => {
    this.props.onDelete(id);
  };
  render() {
    var _p = this.props._p;
    return (
      <div className="row pt-3 pb-5">
        <div className="col-lg-8">
          <h1 className="mt-4">{_p.title}</h1>
          {/*.... POST RELATED INPUTS ....*/}
          <hr />
          {/* Comment Form */}
          <CommentForm postID={_p.id} />
          {/* COMMENT COMPONENT */}
          {_p.comments.map((_c) => (
            <Comment key={_c.id} comment={_c} />
          ))}
        </div>
      </div>
    );
  }
}
export default connect()(Post);

commentForm.js:

class CommentForm extends Component {
  state = {
    name: "",
    commentBody: "",
  };

  static propTypes = {
    addComment: PropTypes.func.isRequired,
  };

  handleSubmit = (e) => {
    e.preventDefault();
    const comment = {
      body: this.state.commentBody,
      post: this.props.postID,
      name: this.state.name,
    };
    this.props.addComment(comment);
  };

  handleChange = (e) => {
    this.setState({
      [e.target.name]: e.target.value,
    });
  };
  render() {
    const { name, commentBody } = this.state;
    return (
      <div className="card my-4">
        <h5 className="card-header">Leave a Comment:</h5>
        <div className="card-body">
          <form onSubmit={this.handleSubmit}>
            <div className="form-group">
              <label>Title:</label>
              <input
                type="text"
                name="name"
                value={name}
                onChange={this.handleChange}
                className="form-control"
              />
            </div>
            <div className="form-group">
              <textarea
                className="form-control"
                name="commentBody"
                value={commentBody}
                onChange={this.handleChange}
                rows="3"
              ></textarea>
            </div>
            <button type="submit" className="btn btn-primary">
              Submit
            </button>
          </form>
        </div>
      </div>
    );
  }
}

export default connect(null, { addComment })(CommentForm);

comment.js:

const Comment = (props) => {
  const _c = props.comment;
  return (
    <div className="media mb-4" key={_c.id}>
      {/* ... COMMENT RELATED OUTPUTS ...*/}
    </div>
  );
};

export default connect(null)(Comment);

reducer:

case ADD_COMMENT:
      state.posts.map((_p) => {
        if (_p.id === action.payload.post)
          _p.comments = [..._p.comments, action.payload];
      });
      return state;

addComment function:

export const addComment = (comment) => (dispatch, getState) => {
  axios
    .post("http://127.0.0.1:8000/api/comments/", comment)
    .then((res) => dispatch({ type: ADD_COMMENT, payload: res.data }))
    .catch((err) => console.log(err));
};

Upvotes: 0

Views: 219

Answers (1)

anncb
anncb

Reputation: 131

Maybe the problem is due to the state that is not considered as modified.

I saw 2 things in your reducer :

  1. You use .map() function, and this function return a new array with the result of your modification. Try to stock this value first.
  2. Then, in your reducer, return a new copy of your state, with the new value of your posts.

Like this :

let modifiedPosts = state.posts.map((_p) => {
        if (_p.id === action.payload.post)
          _p.comments = [..._p.comments, action.payload];
      });

return  Object.assign( {}, state, {
                posts: modifiedPosts 
            } );

Upvotes: 2

Related Questions