I have one component with a form that creates posts and then I have another component that displays these posts in a feed. How do I get the feed to re-render when a new post is created? What's the best way to do that?
- Re-render the parent component without changing props?
- Update props?
- Some other way?
Create.js
import React, { useState } from "react";
export default function Create () {
const [form, setForm] = useState({
post: "",
});
// These methods will update the state properties.
function updateForm(value) {
return setForm((prev) => {
return { ...prev, ...value };
});
}
async function onSubmit(e) {
e.preventDefault();
// When a post request is sent to the create url, we'll add a new post to the database.
const newPost = { ...form };
await fetch("http://localhost:3000/post/add", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(newPost),
})
.catch(error => {
window.alert(error);
return;
});
setForm({ post: "" });
}
return (
<div>
<form onSubmit={onSubmit}>
<div className="form-group">
<label htmlFor="post">Post</label>
<input
type="text"
className="form-control"
id="post"
value={form.post}
onChange={(e) => updateForm( { post: e.target.value })}
/>
</div>
<div className="form-group">
<input
type="submit"
value="Create post"
className="btn btn-primary"
/>
</div>
</form>
</div>
);
}
Feed.js
import React, { useEffect, useState } from "react";
import { Link } from "react-router-dom";
const Post = (props) => (
<div>
{props.post.post}
<Link className="btn btn-link" to={`/edit/${props.post._id}`}>Edit</Link>
<button className="btn btn-link" onClick={() => { props.deletePost(props.post._id);
}}
>
Delete
</button>
</div>
);
export default function PostList() {
const [posts, setPosts] = useState([]);
// This method fetches the posts from the database.
useEffect(() => {
async function getPosts() {
const response = await fetch(`http://localhost:3000/post/`);
if (!response.ok) {
const message = `An error occured: ${response.statusText}`;
window.alert(message);
return;
}
const posts = await response.json();
setPosts(posts);
}
getPosts();
return;
}, [posts.length]);
// This method will delete a post
async function deletePost(id) {
await fetch(`http://localhost:3000/${id}`, {
method: "DELETE"
});
const newPosts = posts.filter((el) => el._id !== id);
setPosts(newPosts);
}
// This method will map out the posts on the table
function postList() {
return posts.map((post) => {
return (
<Post
post={post}
deletePost={() => deletePost(post._id)}
key={post._id}
/>
);
});
}
// This following section will display the the posts.
return (
<div>
<h3>Post List</h3>
<div>{postList()}</div>
</div>
);
}
Home.js
import React from "react";
import Feed from "./feed";
import Create from "./create";
const Home = () => {
return (
<div className="app">
<div>
<Create />
<Feed />
</div>
</div>
);
};
export default Home;
CodePudding user response:
There are numerous ways to do so. The one you should start with is the most straightforward: pass your current posts and setPosts into both component from a parent component:
const Parent = () => {
const [posts, setPosts] = useState([]);
return <>
<Create posts={posts} setPosts={setPosts} />
<PostList posts={posts} setPosts={setPosts} />
</>
}
There are better ways to do so, but they are more involved. I suggest you to start with this one to understand how React handles data better.