Bohdan Skrypnyk
Bohdan Skrypnyk

Reputation: 95

React: updating state after deletion

I'm trying to update elements after deletion, without refreshing a page. Currently, if delete a record, need to refresh a page to see the result. As I understand, need to update useState, but I do not understand how to do it. If I loop useEffect it works but slowly, but I think it's not the best idea to loop get response.

  1. Get all records from a database.
const PostsGetUtill = () => {
    const [posts, setPosts] = useState([]);

    const fetchPosts = () => {
        axios.get("api/v1.0/post/get").then(response => {
            console.log(response.data);
            setPosts(response.data);
        }).catch(function (error) {
            if (error.response) {
                console.log(error.response.data);
                console.log(error.response.status);
                console.log(error.response.headers);
            } else if (error.request) {
                console.log(error.request);
            } else {
                console.log('Error', error.message);
            }
            console.log(error.config);
        });
    };

    useEffect(() => {
        fetchPosts();
    }, []);           //  }, [fetchPosts]); <--- working well with loop 

    return (
        <section className="container-post">
            <PostMansonry posts={posts} columns={3} />
        </section>
    );
};

export default PostsGetUtill;
  1. Sort and map records
export default function PostMansonry({ posts, columns }) {
    return (
        <section className="masonry" style={{ gridTemplateColumns: `repeat(${columns}, minmax(275px, 1fr))` }}>
            {posts.sort((a, b) => a.zonedDateTime < b.zonedDateTime ? 1 : -1).map((posts, index) =>
                <MasonryPost {...{ posts, index, key: index }} />)
            }
        </section>
    )

}
  1. Put data to the card
export default function MasonryPost({ posts, index }) {
    return (
        <div key={index} className="masonry-post">
            <div className="card">
                <div className="card-body">
                    <h5 className="card-title">{posts.title}</h5>
                    <p className="card-text">{posts.description}</p>
                    <p className="card-text"><small className="text-muted"> {posts.zonedDateTime}</small></p>
                    <div><button type="button" onClick={(e) => PostsDeleteUtill(posts.post_Id)} className="btn btn-danger">Delete</button></div>
                </div>
            </div>
        </div>
    )
}

  1. Deleting
const PostsDeleteUtill = async (post_Id) => {
    axios.delete(`api/v1.0/post/delete/${post_Id}`).then(response => {
        console.log(response);
    }).catch((error) => {
        if (error.response) {
            console.log(error.response.data);
            console.log(error.response.status);
            console.log(error.response.headers);
        } else if (error.request) {
            console.log(error.request);
        } else {
            console.log('Error', error.message);
        }
        console.log('error config', error.config);
    });
};

export default PostsDeleteUtill;

Upvotes: 1

Views: 256

Answers (1)

Bruno Monteiro
Bruno Monteiro

Reputation: 4519

Basically what you need to do is, in your PostsDeleteUtill function, in the promise return of your axios.delete, you need to update your posts state, which is set in PostsGetUtill.

In order to do that, you have 2 options:

  1. Use a global state (React Context, Redux, etc)
  2. Pass your setPosts handle all the way to your PostsDeleteUtill

I think option 1 is a bit cleaner for your specific case, but if you don't need global state anywhere else in your project, maybe it is fine to have a not so clean solution instead of implementing the whole global state structure for only one thing.

Option 1 pseudo code:

Your PostsGetUtill component would use a global state instead of local state:

const PostsGetUtill = () => {
    // Remove this:
    // const [posts, setPosts] = useState([]);

    const fetchPosts = () => {
        axios.get("api/v1.0/post/get").then(response => {
            console.log(response.data);
            // Instead of a local "setPosts" you would have a global
            // "setPosts" (in Redux, this would be a dispatch)
            dispatch({type: "PUT_POSTS", action: response.data})
        }).catch(function (error) {
            // No changes here...
        });
    };

    // This runs only the first time you load this component
    useEffect(() => {
        fetchPosts();
    }, []); 

    // Use your global state here as well:
    return (
        <section className="container-post">
            <PostMansonry posts={globalState.posts} columns={3} />
        </section>
    );
};

export default PostsGetUtill;

In your PostsDeleteUtill function:

const PostsDeleteUtill = async (post_Id) => {
    axios.delete(`api/v1.0/post/delete/${post_Id}`).then(response => {
        // Update global state here. Probably filter the data to remove
        // the deleted record
        const updatedPosts = globalState.posts.filter(post => post.id !== response.data.id)
    }).catch((error) => {
      // No changes here
    });
};

export default PostsDeleteUtill;

Option 2 pseudo code:

In your PostsGetUtill component, create and pass on a handleRemovePost:

// Your existing code ...
const handleRemovePost = (postID) => {
    const filteredPosts = posts.filter(post => post.id !=== postID)
    setPosts(filteredPosts)
}

return (
    <section className="container-post">
        <PostMansonry posts={posts} columns={3} handleRemovePost={handleRemovePost} />
    </section>
);

In your PostMansonry, pass on again your handleRemovePost

export default function PostMansonry({ posts, columns, handleRemovePost }) {
    return (
        // Your existing code ...
        <MasonryPost {...{ posts, index, key: index, handleRemovePost }} />)
    )
}

Again in your MasonryPost

export default function MasonryPost({ posts, index, handleRemovePost }) {
    return (
        // Your existing code ...
        <button type="button" onClick={(e) => PostsDeleteUtill(posts.post_Id, handleRemovePost)} className="btn btn-danger">Delete</button>
    )
}

And finally:

const PostsDeleteUtill = async (post_Id, handleRemovePost) => {
    axios.delete(`api/v1.0/post/delete/${post_Id}`).then(response => {
        handleRemovePost(response);
    })
};

PS: Please note that I only added a pseudo-code as a reference, trying to point out specific parts of the code that needs to be updated. If you need more information about global state, you can check React Context and Redux

Upvotes: 1

Related Questions