I have 2 components, one that is used to add new post to an array of posts and another component that maps through that array of posts and lists them on the page.
I want to add the new post to the top of the array with unshift()
, but ever since componentWillReceiveProps
was deprecated, I've been struggling to find a solution with the new getDerivedStateFromProps
method.
Here is Postform.js:
import React, { Component } from "react";
import { connect } from "react-redux";
import PropTypes from "prop-types";
import { createPost } from "../actions/postActions";
class PostForm extends Component {
constructor(props) {
super(props);
this.state = {
title: "",
body: "",
};
this.onChange = this.onChange.bind(this); // Bind onChange event
this.onSubmit = this.onSubmit.bind(this); // Bind onSubmit event
}
// Set state when changing input value
onChange(e) {
this.setState({ [e.target.name]: e.target.value });
}
onSubmit(e) {
e.preventDefault();
const post = {
title: this.state.title,
body: this.state.body,
};
this.props.createPost(post);
}
render() {
return (
<div>
<h1>Add Post</h1>
<form onSubmit={this.onSubmit}>
<div>
<label>Title: </label>
<br />
<input
type="text"
name="title"
onChange={this.onChange}
value={this.state.title}
/>
</div>
<br />
<div>
<label>Body: </label>
<br />
<textarea
name="body"
onChange={this.onChange}
value={this.state.body}
/>
</div>
<br />
<button type="submit">Submit</button>
</form>
</div>
);
}
}
PostForm.propTypes = {
createPost: PropTypes.func.isRequired,
};
export default connect(null, { createPost })(PostForm);
Here is Posts.js:
import React, { Component } from "react";
import PropTypes from "prop-types";
import { connect } from "react-redux";
import { fetchPosts } from "../actions/postActions";
class Posts extends Component {
state = {
newPost: this.props.newPost,
};
componentDidMount() {
this.props.fetchPosts();
}
// The new way I am struggling with
static getDerivedStateFromProps(props, state) {
if (props.newPost !== state.newPost) {
props.posts.unshift(props.newPost);
}
return null;
}
// The old way, now renamed with UNSAFE_ prefix
/* UNSAFE_componentWillReceiveProps(nextProps) {
if (nextProps.newPost) {
this.props.posts.unshift(nextProps.newPost);
}
} */
render() {
const postItems = this.props.posts.map((post) => (
<div key={post.id + Math.random()}>
<h3>{post.title}</h3>
<p>{post.body}</p>
</div>
));
return (
<div>
<h1>Posts</h1>
{postItems}
</div>
);
}
}
Posts.propTypes = {
fetchPosts: PropTypes.func.isRequired,
posts: PropTypes.array.isRequired,
newPost: PropTypes.object,
};
const mapStateToProps = (state) => ({
posts: state.posts.items,
newPost: state.posts.item,
});
export default connect(mapStateToProps, { fetchPosts })(Posts);
The problem is that
getDerivedStateFromProps
is called twice (initial mount and every render),
unlike componentWillReceiveProps
which is called only once.
This causes the newPost to be added twice to the array and thus it shows double on the page too.
I am using Redux here, so the state should be in the store (mostly is), but I've added a state for the Posts.js
component since the getDerivedStateFromProps
won't otherwise work (as far as I've tried).
In a nutshell: How do I add the newPost to the props-array inside the getDerivedStateFromProps
(or other lifecycle method) without getting double results?