0

I have looked into many articles and posts like this but it does not work in my case.I simply need to delete an item from my post list in my application using axios. In the axios docs it says you need to pass in the params to the delete method. Also I have sen in most apps they use ids without having ids in their state. But i cannot get it working. Please see my entire code. I know that my delete method is wrong please help me fix it:

    // The individual post component
    const Post = props => (
    <article className="post">
        <h2 className="post-title">{props.title}</h2>
        <hr />
        <p className="post-content">{props.content}</p>
        <button onClick={props.delete}>Delete this post</button>
    </article>
);

// The seperate form component to be written later

class Form extends React.Component {}

// The posts loop component

class Posts extends React.Component {
    state = {
        posts: [],
        post: {
            title: "",
            content: ""
        }
        // error:false
    };

    componentDidMount() {
        const { posts } = this.state;
        axios
            .get("url")
            .then(response => {
            const data = Object.values(response.data);
            this.setState({ posts : data });
            });
    }
    handleChange = event => {
        const [name , value] = [event.target.name, event.target.value];
        // const value = event.target.value;
        const { post } = this.state;
        const newPost = {
            ...post,
            [name]: value
        };
        this.setState({ post: newPost });
    };

    handleSubmit = event => {
        event.preventDefault();
        const {post} = this.state;
        const {posts} = this.state;
        axios
            .post("url", post)
            .then(response => {
            // console.log(response);
            const newPost = Object.values(response.data);
            this.setState({ post: newPost });
            const updatedPosts =  [...posts, {title:post.title,content:post.content}];
            this.setState({ posts: updatedPosts});
            // console.log(post);
            console.log(updatedPosts);
            console.log(this.state.posts);
            });
    };

    handleDelete = () => {
        const { post } = this.state;
        axios.delete("url",{params: {id: post.id}})
        .then(response => {
            console.log(response);
        });
    };

    render() {
        let posts = <p>No posts yet</p>;
        if (this.state.posts !== null) {
            posts = this.state.posts.map(post => {
                return <Post 
                                 key={post.id} 
                                 {...post}
                                 delete={this.handleDelete}/>;
            });
        }

        return (
            <React.Fragment>
                {posts}
                <form className="new-post-form" onSubmit={this.handleSubmit}>
                    <label>
                        Post title
                        <input
                            className="title-input"
                            type="text"
                            name="title"
                            onChange={this.handleChange}
                        />
                    </label>
                    <label>
                        Post content
                        <input
                            className="content-input"
                            type="text"
                            name="content"
                            onChange={this.handleChange}
                        />
                    </label>
                    <input className="submit-button" type="submit" value="submit" />
                </form>
            </React.Fragment>
        );
    }
}

I also see this Error in console: Uncaught (in promise) TypeError: Cannot convert undefined or null to object at Function.values which is in get method. Thanks again.

azad6026
  • 139
  • 1
  • 3
  • 11
  • 4
    Axios is the HTTP client for communicating with your API. How you delete a post via your API has nothing to do with React. Whether or not the DELETE method is even accepted by your API or the right call depends on how the API is designed. – wdm Oct 08 '18 at 00:23
  • I am using firebase for my database.It accepts it. My question was if my delete method is right in syntax. How do I give the delete method the selected post ? – azad6026 Oct 08 '18 at 02:17
  • If you are using Firebase why are you using Axios to delete a POST in Firebase? It would be much simpler to just use Firebase Javascript SDK or some NPM package like re-base. – Simon Cadieux Oct 08 '18 at 02:47
  • I need and want to use axios. please help me with the delete syntax un my app. everything else works fine. – azad6026 Oct 08 '18 at 03:33
  • Hmm... this code is very confusing. First of all, if everything worked fine, you would not get an error in the get method. What is your intention with using `Object.values()` to create an array? In `handleChange` it is set as an object and in `handleSubmit` it is an array. I've never seen this pattern. We will get to axios after sorting that one out. – Avin Kavish Oct 08 '18 at 04:05
  • No I don't have any error in my app without the delete section. I can get data and post data with no problems. sinceI added the delete section I am getting error. I am using the Object.values because my response is like an array of objects. each object contains of an index as key and my single post as value. So I want to pull out only the values which are my single posts and create an array of those to update my state.That works fine and without that I get an error in my map function saying posts is not an array to use map on.Is there a better way?Now I just need to know how to delete an item. – azad6026 Oct 08 '18 at 04:44

2 Answers2

7

You are not specifying what your Post component should delete. In other words, the props.delete is not receiving an id to pass up to your parent component. In order to do that, you can change that to () => props.delete(props.id) and then in your parent component you need to have the handleDelete method receive the id of the item you want to target which is the id we passed up earlier from Post.

I don't know how your server is set up but using the axios request you originally have in your question your code would look like this:

handleDelete = (itemId) => {
    // Whatever you want to do with that item
    axios.delete("url", { params: { id: itemId } }).then(response => {
      console.log(response);
    });

Here's a CodeSandbox (using some dummy data in the constructor) displaying the item being passed in a console.log() (axios statement is commented out).


EDIT: How to make axios delete requests using Firebase REST API

Oh sorry, I did not see that you were using Firebase. Direct REST requests are a bit different with Firebase. In your configuration the requests should look like this:

axios.delete(`${url}/${firebasePostId}.json`).then(response => {
    console.log(response)
})

This is assuming your Firebase rules allow unauthorized requests (which I strongly advise against, seeing as anyone could send this request).

Please note that firebasePostId is the push key provided by Firebase when you send POST requests to them, and are in fact a great choice of id for your posts. An example of one is -LOLok8zH3B8RonrWdZs which you mentioned in the comments.

For more information on Firebase REST API syntax, check out their documentation.

  • Thanks but I don't have an id and that is the main thing. How should I add id and then use it. In your code it is hard coded. – azad6026 Oct 08 '18 at 10:24
  • There are many ways to add unique `id`s to your posts. One of the easier ways of doing that is by using a timestamp via `Date.now()`. I have demonstrated this in this [CodeSandbox](https://codesandbox.io/s/o5w3pylwzz). However, I recommend using a library like [uuid](https://www.npmjs.com/package/uuid) for `id` generation purposes. – Franklin Farahani Oct 08 '18 at 18:16
  • Thanks. I created the id for each post just temporarily using my posts.length. But my question from the begening was what is the SYNTAX for delete method. In axios docs (https://github.com/axios/axios#axiosdeleteurl-config) it says to use config and params and data which is very confusing and there is no example anywhere really. – azad6026 Oct 09 '18 at 00:56
  • This is my delete method: handleDelete = postId => { axios.delete("url",{params : {id : postId }}) .then(response => { console.log(response); this.setState({ posts: posts }); }); } and postId comes from my single post. : this.handleDelete(post.id)} /> Inside database and in my template I have same ids as when I post data I set the id as part of my post: post = { id, title, content} Now how can i delete a post use its unique id??Thanks. – azad6026 Oct 09 '18 at 00:58
  • Axios performs `DELETE` requests via parameters instead of JSON objects. You essentially tell it to find the item with this `id` and delete it. As far as I can tell, your request's syntax is correct. However your server needs to be set up to accept what you send in your request, which in your case is `{id : postId }` from: `{params : {id : postId }}`. If you are seting up the server yourself, you have to specify this operation of finding the item with that `id` and deleting it, in there. What kind of server are you working with? – Franklin Farahani Oct 09 '18 at 01:35
  • As I mentioned I use firebase and this is my db structure for posts: { "-LOLojEPncynvcVl06fW" : { "content" : "some content", "id" : 1, "title" : "title1" }, "-LOLok8zH3B8RonrWdZs" : { "content" : "some content", "id" : 2, "title" : "title2" } } – azad6026 Oct 09 '18 at 03:18
  • The delete method deletes all posts in database when I click the button on any of the posts. No the specific item I am clicking on.It actually does not take id. – azad6026 Oct 09 '18 at 04:19
  • Yes, since Firebase REST API syntax is a bit different. I edited the original answer to expand on the solution. – Franklin Farahani Oct 09 '18 at 04:39
  • Sorry @FranklinFarahani. Thanks for that I put up my comment as an answer as it is very long to explain – azad6026 Oct 09 '18 at 22:34
0

Thanks @FranklinFarahani. I had to write an answer as it is too long. I have changed my get and post method and managed to fix the delete method. I use the unique key that firebase is creating per post to delete each item. I get that inget method. This is the entire code.

 // The individual post component
  const Post = props => (
    // use the key as an id here
    <article id={props.id} className="post">
        <h2 className="post-title">{props.title}</h2>
        <hr />
        <p className="post-content">{props.content}</p>
        <button onClick={props.delete}>Delete this post</button>
    </article>
);

// The Post lists component

class Posts extends React.Component {
    state = {
        posts: [],
        post: {
            id: "",
            title: "",
            content: ""
        },
        indexes: []
    };

    componentDidMount() {
        const { posts } = this.state;
        axios
            .get("firebaseURL/posts.json")
            .then(response => {
              // create an array to hold th unique id as key and post as value using Object.entries
                const retrievedPosts = [];
                for (const [key, value] of Object.entries(response.data)) {
                    const post = {
                        id: key,
                        title: value.title,
                        content: value.content
                    };
                    // add allposts to the array here
                    retrievedPosts.push(post);
                }
                // update state
                this.setState({ posts: retrievedPosts });
            console.log(retrievedPosts);
            });
    }
    handleChange = event => {
        const [name, value] = [event.target.name, event.target.value];
        // const value = event.target.value;
        const { post } = this.state;
        const newPost = {
            ...post,
            [name]: value
        };
        this.setState({ post: newPost });
    };


    handleSubmit = event => {
        event.preventDefault();
        const { posts } = this.state;
        // use this as a temporary id for post method
        const postIndex = posts.length + 1;
        const post = {
            id: postIndex,
            title: this.state.post.title,
            content: this.state.post.content
        };
        axios
            .post("firebaseURL/posts.json", post)
            .then(response => {
                const updatedPosts = [
                    ...posts,
                    { id: post.id, title: post.title, content: post.content }
                ];
            // update state
                this.setState({ posts: updatedPosts });
            console.log(posts);
            });

    };

    handleDelete = postId => {
        event.preventDefault();
        // get a copy of the posts
        const posts = [...this.state.posts];
        // in delete method use postId to create a unique url for the post to be deleted
        axios
            .delete(
                "firebaseURL/posts/" + postId + ".json"
            )
            .then(response => {
            //update state
                this.setState({ posts: posts });
            });
    };

    render() {
        let posts = <p>No posts yet</p>;
        if (this.state.posts !== null) {
            posts = this.state.posts.map(post => {
                return (
                    <Post
                        id={post.id}
                        key={post.id}
                        {...post}
                        delete={() => this.handleDelete(post.id)}
                    />
                );
            });
        }

        return (
            <React.Fragment>
                {posts}
                <form className="new-post-form" onSubmit={this.handleSubmit}>
                    <label>
                        Post title
                        <input
                            className="title-input"
                            type="text"
                            name="title"
                            onChange={this.handleChange}
                        />
                    </label>
                    <label>
                        Post content
                        <textarea
                            className="content-input"
                            rows="7"
                            type="text"
                            name="content"
                            onChange={this.handleChange}
                        />
                    </label>
                    <input className="submit-button" type="submit" value="submit" />
                </form>
            </React.Fragment>
        );
    }
}

The problem is my state is not updated after delete so although the post has been deleted from my database it is still in the DOM.

And more importantly if submit a new post cannot delete it after a get request or refresh being done. The reason is in post request the key will be created after the request is done and therefor I will not have the key to update state and DOM until after the next get request or refresh. And the id will be the temporary one which I assign during post method which cannot be used to delete a post.

Samuel Liew
  • 76,741
  • 107
  • 159
  • 260
azad6026
  • 139
  • 1
  • 3
  • 11
  • I know of a couple ways to fix this problem. The less elegant of the two is to move your current GET request logic in `componentDidMount` into a new class method, and then call this method once when your component mounts, once right after you perform your POST requests, and once right after you perform your DELETE request. The other solution is to stream your db changes back to your app. I personally do not have any experience with that but it does not seem too complicated. You can refer to the links in my next comment on how to do that. – Franklin Farahani Oct 10 '18 at 01:51
  • [Streaming from the REST API](https://firebase.googleblog.com/2014/03/announcing-streaming-for-firebase-rest.html) | [Announcing Streaming for the Firebase REST API](https://firebase.googleblog.com/2014/03/announcing-streaming-for-firebase-rest.html) – Franklin Farahani Oct 10 '18 at 01:52
  • Thanks @FranklinFarahani. I will look into that. I will make your answer the accepted answer. I have corrected my code by your help and put it in this answer to help others perhaps.Thanks again.I will make another question for updating state if needed. – azad6026 Oct 10 '18 at 08:35