Given the following form, I need whenever the form is submitted, the new post to be listed/rendered without having to refresh the page.
const PostCreate = () => {
const [title, setTitle] = useState('');
const onSubmit = async (event) => {
event.preventDefault();
await axios.post(`http://${posts_host}/posts/create`, {title}).catch(error => {
console.log(error)
})
setTitle('');
};
return (<div>
<form onSubmit={onSubmit}>
<div className="form-group">
<label>Title</label>
<input value={title} onChange={event => setTitle(event.target.value)}
className="form-control "/>
</div>
<button className="btn btn-primary">Submit</button>
</form>
</div>)
}
export default PostCreate;
I tried adding this.forceUpdate()
and this.setState(this.state)
, neither works, and I still have to refresh the page for the new post to show.
Here's how the posts are rendered:
const PostList = () => {
const [posts, setPosts] = useState({});
const fetchPosts = async () => {
await axios.get(`http://${queries_host}/posts`).then(response => {
setPosts(response.data);
}).catch(error => {
console.log(error)
});
};
useEffect(() => {
fetchPosts();
}, []);
const renderedPosts = Object.values(posts).map(post => {
return <div className="card"
style={{width: '30%', marginBottom: '20px'}}
key={post.id}>
<div className="card-body">
<h3>{post.title}</h3>
<CommentList comments={post.comments}></CommentList>
<CommentCreate postId={post.id}></CommentCreate>
</div>
</div>
});
return <div>
{renderedPosts}
</div>;
}
export default PostList;
This is what App.js
looks like
const App = () => {
return <div>
<h1>Create Post</h1>
<PostCreate></PostCreate>
<hr/>
<h1>Posts</h1>
<PostList></PostList>
</div>;
};
export default App;
and is eventually rendered using:
ReactDOM.render(
<App></App>,
document.getElementById('root')
)
CodePudding user response:
I think the problem you are having is not in the code you have displayed. The component is indeed rerendering after you change its state and also when you forceUpdate() it. I assume the posts you are trying to display are taken from the same API that you post to. Even if this component is being rerendered, your GET request which gives the data to the component who renders it is not called again so the data doesn't update. You need to refetch it. This can be done by many different ways (useEffect(), callbacks, reactQuery refetch) depending on the rest of your code. I would need the component that renders the data and the API call to help you further.
Another thing that you didn't ask but is good practice. In your PostCreate component you don't need to manage the state of fields that are in the form, because it already does it for you. Just give a name to your inputs and use the form data. I've given an example below.
import { useState } from "react";
const PostCreate = () => {
const onSubmit = async (event) => {
event.preventDefault();
console.log(event.target.elements.title.value);
};
return (
<div>
<form onSubmit={onSubmit}>
<div className="form-group">
<label>Title</label>
<input name="title" className="form-control" />
</div>
<button className="btn btn-primary">Submit</button>
</form>
</div>
);
};
export default PostCreate;
CodePudding user response:
In your PostList
, useEffect called once when you first load your component, so when you create new post, it will not be re-rendered
You should bring your fetchPost
logic to your App component, and add function props onPostCreated
to PostCreate
component, trigger it after you finish creating your new post
The code should be:
const App = () => {
const [posts, setPosts] = useState({});
const fetchPosts = async () => {
await axios.get(`http://${queries_host}/posts`).then(response => {
setPosts(response.data);
}).catch(error => {
console.log(error)
});
};
useEffect(() => {
fetchPosts();
}, []);
return <div>
<h1>Create Post</h1>
<PostCreate onCreatePost={() => fetchPost()}></PostCreate>
<hr/>
<h1>Posts</h1>
<PostList posts={posts}></PostList>
</div>;
};
export default App;
const PostList = ({ posts }) => {
const renderedPosts = Object.values(posts).map(post => {
return <div className="card"
style={{width: '30%', marginBottom: '20px'}}
key={post.id}>
<div className="card-body">
<h3>{post.title}</h3>
<CommentList comments={post.comments}></CommentList>
<CommentCreate postId={post.id}></CommentCreate>
</div>
</div>
});
return <div>
{renderedPosts}
</div>;
}
export default PostList;
const PostCreate = ({ onCreatePost }) => {
const [title, setTitle] = useState('');
const onSubmit = async (event) => {
event.preventDefault();
await axios.post(`http://${posts_host}/posts/create`, {title}).catch(error => {
console.log(error)
})
onCreatePost && onCreatePost();
setTitle('');
};
return (<div>
<form onSubmit={onSubmit}>
<div className="form-group">
<label>Title</label>
<input value={title} onChange={event => setTitle(event.target.value)}
className="form-control "/>
</div>
<button className="btn btn-primary">Submit</button>
</form>
</div>)
}
export default PostCreate;