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);
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?
getDerivedStateFromProps is invoked right before calling the render method, both on the initial mount and on subsequent updates. It should return an object to update the state, or null to update nothing as per react docs : https://reactjs.org/docs/react-component.html#static-getderivedstatefromprops
You already seem to be using redux, thus once you have done createPost why don't you do array.unshift while updating your posts in the Redux Store and thus your store will hold the state.posts.items with your latest post on top.