Deleting Featured Blog Images via API and Updating Parent Component State
We're getting pretty close to having this entire portfolio blog completed, and so in this guide, we're going to take what we've built for deleting the image, and we're actually going to take it live, so we can click on that Remove Image, or Remove File link, and it will work.
Guide Tasks
  • Read Tutorial
  • Watch Guide Video
Video locked
This video is viewable to users with a Bottega Bootcamp license

So we're going to replicate quite a bit of the work we did with the Portfolio Form, the Portfolio Manager Form, and we're going to add that into our blog. So let's get started, right here, you can see the page we'll be working with, and the end goal for this guide is that we should be able to click on this Remove File link, and it should go and it should delete the file on the server and then auto-render the react-dropzone component right there.

So let's get started, and let's build this out. So, inside of the portfolio-form, as review, you can see, we have our deleteImage function. And we're gonna be able to use this as a model because the behavior we're trying to implement is pretty much identical.

So, I will add one caveat. Any time that you copy and paste code from another part of your application, you have a couple questions to ask yourself. One is, do you really need to copy and paste this? Remember, there is the DRY principle, which stands for Do not Repeat Yourself, and so in many cases, if you find yourself copy and pasting a lot of code from other parts of the application that's usually giving you a pretty good hint that that gives you a refactor opportunity.

Now, due to time constraints and things like that, we are not going to perform every single refactor in this course because, one, I want you to be able to move on to more advanced material, but also so that you have your own ability and your own time to go and perform refactors, and I definitely will give you hints on when you might want to do that, and this is one of those hints.

So right here, with our portfolio-form, we are going to take all of this functionality here, and we're going to copy it, and then we're going to pass it into the Blog Form. Now, if we were in a refactor course or something like that, what my recommendation would be, would be to create a helper function, or a component that manages this process, because if you notice the key differences here, we only have a few of 'em. One is the URL, and then two is what you want to do with the response.

So in this case, for our portfolio-form, we simply want to update the local state, whereas with the Blog Form, we're gonna wanna perform a different task. We're gonna call a prop that we're going to create in this guide. And so, due to time, we're not gonna go through the entire process of creating its own component, or helper method, but, that's something I definitely recommend.

It'd be good practice, after we get this feature working, to go through, create a method, and then be able to call that from both of these components so that you can just make your code a little bit more efficient. It's not required whatsoever, but, anytime I do see a part of an application that I think could be written a little bit more efficiently, I always want to point that out.

So let's go into our Blog Form, and we're going to simply paste this in. So we have our deleteImage function, and let's also call that and bind it to this, so I'm gonna say this.deleteImage = and then we'll say this.deleteImage.bind this, and I'm also gonna move this up, just for code organization purposes. You can see that we have all of our image items, all now grouped together. So I like that because now I can come, and I can see exactly the full list of our functions that relate to images.

this.componentConfig = this.componentConfig.bind(this);
this.djsConfig = this.djsConfig.bind(this);
this.handleFeaturedImageDrop = this.handleFeaturedImageDrop.bind(this);
this.deleteImage = this.deleteImage.bind(this);
this.featuredImageRef = React.createRef();

Now in deleteImage, you have a couple options here. So, one thing that we could do, because we only have one image type, we could get rid of this argument, and we could just hard code this into the URL, that's perfectly fine if you wanna do that. I am gonna leave it here, and it is solely for the reason that I want to give you a little bit of a head start when it comes to performing a refactor here if you'd like to.

If you'd like to go create your own helper method, kind of like what we did with the icons, and that kind of thing, then I wanna keep the API here as close as possible, because if I can keep both of these functions nearly identical with just a few key changes, then you're not gonna have to do quite as much work, so that's the only reason why I will keep that there.

And now, in the delete function, we need to update this URL. It's no longer gonna be delete-portfolio-image, what it's going to do is it's gonna be, delete-portfolio-blog-image. And then we are still passing in the ID, but this time, we're not passing in the state ID, we're gonna pass in the props.blog ID, and then the image type will be the same.

And we're also passing withCredentials, and then we need to, here, in the response, what we need to do is remove the setState, 'cause we're not update being state, what we need to do is go up to our parent component, to the blog detail, and update that. So I'm just gonna leave a little TODO note right here, and we're just gonna console.log out whatever this response from blog image delete is going to be, just so we can see what that response is, and that's all we need to do there until we build that prop, that functional prop that we're gonna pass in.

blog-form.js

deleteImage(imageType) {
  axios
    .delete(
      `https://api.devcamp.space/portfolio/delete-portfolio-blog-image/${
        this.props.blog.id
      }?image_type=${imageType}`,
      { withCredentials: true }
    )
    .then(response => {
      // TODO
      console.log("response from blog image delete", response);
    })
    .catch(error => {
      console.log("deleteImage error", error);
    });
}

So now that we have that, we can come down here into the actual JSX code itself, and we can call this. So if you come all the way down to where we have our image, and we have our Remove file, this is where we can simply call our onClick handler, and then inside of onClick, we're going to pass in an anonymous function.

So make sure you're passing in an arrow function, and I'm gonna say this.deleteImage, and right here we can hard code it and just say this is our featured_image. So that is the one that we're wanting to delete.

<div className="image-removal-link">
  <a onClick={() => this.deleteImage("featured-image")}>
    Remove file
  </a>
</div>

Let's see, let's test this out, and see if this is working. So make sure you hit Refresh, and also open up your Console. Now if you switch to Edit mode, and you click on Remove File, it looks like that worked. You have the Console log here that says response from blog image delete, and you can see we got a status of 204.

large

That's working perfectly, that's exactly what we wanted, that is the HTTP verb for the delete working, and so, if I close out of the Console here, you'll see our featured image is gone.

large

So we've completed the first half of this. We've communicated with the API, we've told it, okay, we do not want this featured image anymore. Now let's take care of the next step. The next step is reaching up into our blog detail and saying, hey we got rid of this blog featured image, we need you to update your own state.

So let's get started on that. Switching over into Blog Detail, if you scroll all the way up to the top here, we're going to create a new function called this.handleFeaturedImageDelete, and we'll bind that to this.handleFeaturedImageDelete, bind it to this, and now we can implement this function. It's gonna be relatively straightforward, it's not gonna take in any arguments, and what we're gonna do is reach into the blogItem in our state, and remove that.

So I can call this.setState, and then inside of our blogItem object, I can pass in the clearing code. So here we have blogItem, we know that we have a featured_image_url, and we're gonna set that equal to an empty string.

And then we also have to pass this in as a functional prop into our blog form, so if you come all the way down to our blog form, you can see we have editMode, and blog. Now we can simply pass in this function, handleFeaturedImageDelete =this.handleFeaturedImageDelete,

blog-detail.js

const contentManager = () => {
  if (this.state.editMode) {
    return (
      <Blogform
      handleFeaturedImageDelete={this.handleFeaturedImageDelete}
      editMode={this.state.editMode}
      blog={this.state.blogItem}
    />
    );
  }

and now we can call this from our Blog Form. So now, where we used to in the Portfolio Form, where we said set.state, now we will just call this functional prop. So scroll up to wherever you put the delete the featured image function or deleteImage here, and right under our TODO, I can say this.props.handleFeaturedImageDelete. Call that function, I'll get rid of our TODO note, and our console.log, hit Save, and let's come back here and see if this is working.

blog-form.js

.then(response => {
  this.props.handleFeaturedImageDelete();
})

We need to add an image, and so I'm going to just create a new post, 'cause we haven't added our Edit feature yet, and this will, let's say, testing with image, blog status is draft, doesn't matter what you put in your content, hit save, and now, if you click testing with image, click on your Edit Mode function, and now click Remove File. You can see that worked, we still have all of our content, but now we have cleared that out of our state, out of our parent state.

large

So we no longer have a featured image. We already know that the image is gone. So if I hit Refresh right here, you'll see that it is no longer there, but now we've cleared it out of state, so that we're following the best user pattern. So if you click on Delete Image, the pattern you're really looking for, is not to just delete the image on the server, it is also to update the state, take advantage of the reactive nature of React so that the page can rerender in the spots where you want it to, and it gives the users to make other decisions.

So say that you created a blog post, and that you uploaded it, and then later on, you want to change the featured image. You shouldn't have to redo that entire blog post, you should be able to delete the image, have the dropzone component rendered right there, like we saw, and then upload a new image. And that's exactly the behavior that we now have.

So, we have each one of the components needed for the Blog Form to work, and that is looking great. In the next guide, we're gonna connect everything. So we still have a few small missing pieces that haven't been wired up, we haven't added things such as our base content for Edit Mode, and our blog status, and so we need to take all of that and we need to be able to dynamically have our Axios call to the API work for either POST requests, for when we want to create a new blog, and then also PATCH requests, when we want to edit one and then update the state.

So in the next guide, we're gonna put all of that together and finish out this entire Edit feature.

Resources