reactjsformsonsubmit

How to render form after submission in react?


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')
)

Solution

  • 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;