Remove Items in a List Without Mutating the Source Array

Andy Van Slaars
InstructorAndy Van Slaars

Share this video with your friends

Send Tweet
Published 3 years ago
Updated 2 years ago

In React, we try to avoid mutating data structures and instead, treat them as immutable. This means an update to an object or an array should result in a new object or array that reflects the updates we want to make. This allows React to use reference comparison for optimization purposes. We don't always need this, but if we are consistent in treating data as immutable, we can take advantage of that whenever we need it without having to go back and change existing code. In this lesson, we'll see how we can remove an item from an array without mutating the original array.

Andy Van Slaars: [0:00] I've updated the cardService module to include this destroyCard method that will make a fetch call to remove an item from our database. Let's wire this up to our app so that our delete button for each of our flashcards will remove the item from the database on the back end and update our UI.

[0:14] Let's go into the cardPreview component. At the top of this function I'm going to declare a new function and I'm going to call it handleDelete. This is where we'll handle the click for our delete button. Let's take this function and we're going to come down to our delete button. Then we're going to add an onClick. onClick is going to call handleDelete.

[0:40] Now that that's wired up let's come up here and add some functionality to handleDelete. Our handleDelete function needs to do a few things. First, we want to confirm the delete. Second, we want to call the API. Third, when that's successful we want to notify the app component that this delete happens so that it can remove the local copy of the flashcard from state.

[1:06] We'll implement these pieces slightly out of order starting with notifying the parent component. Let's leave card preview for now. Let's switch over the app.js. In app.js I want to add a new function. This function is going to be called handleRemove.

[1:27] HandleRemove is not responsible for calling our API. We'll do that from the component. Here we're just going to remove the deleted card from our local state. I'm going to set up handleRemove to accept an ID property.

[1:40] Then we can update our local cards. To do that, we're going to call setCards. That's going to give us the existing cards. Then in this function we'll return the new value. We'll take our existing cards and we'll call filter on those. Then we're going to filter each card. We're going to keep a card as long as its ID doesn't match the passed in ID. This will effectively remove the card with this ID from our list.

[2:07] From here I'm going to go down to my render. When I map over these cards, I need to do a couple of things. I'm going to start by passing the ID in as a prop. We're currently using it as the key, but we need the component to know about its own ID. We'll pass that in as a prop called ID. Then we're going to pass in an onRemove handler. onRemove is going to be a reference to the handleRemove function that we justified.

[2:32] We can save that. Now we can go back to cardPreview. In our handleDelete function let's start by calling props.onRemove and passing in props.ID. With this in place, let's run it in the browser and see where we're at. I'm going to save this and in the terminal I'll start this up with yarn start.

[2:57] Now it's reloaded, I'm going to click one of the delete buttons and we'll see the item we clicked on went away. Now if I reload the page, we'll re-fetch that because we never made the API call to delete it on the server, but we know our UI state update works. Let's go back and finish this feature up.

[3:15] The first thing we want this function to do is confirm the delete. Let's do that and define a const, I'm going to call it confirm, and we're going to set that to equal window.confirm. Then we use string interpolation.

[3:33] We'll put the term from the card in here, so we'll do props.term. We can even close that in quotes. Then we're going to wrap the rest of this in an if statement and say if confirm, then we want to do the remaining steps, so we'll move those in there.

[3:53] Let's add the call for our API. I'm going to jump to the top of the file and I'm going to start by importing destroyCard from services cardService. I can come back down to my function here and we'll call destroyCard, passing in props.ID. That's going to return a promise, so we'll call then. In the callback for then, we'll notify the app component that the item should be removed from the UI.

[4:32] Let's save this and take a look in the browser. I'm going to switch to the network tab and I'm going to clear it out. I'm going to click delete on this first item and we'll see that we get our confirmation. If we hit cancel, then nothing should happen.

[4:46] I'll click delete again and this time I'll confirm. We'll see in our network tab that our call was made to the card endpoint with our ID with the method of delete and our item was removed from our UI. Now it's been removed from the back end and the UI has reflected that change.

[5:03] Let's go back to our code and let's clean up some of these comments. We'll get rid of this. The only other change I'd like to make is I'd like to verify that for a callback prop that it is present, that it's actually a function, and then I make the call.

[5:21] I'm just going to do props.onRemove double ampersand and then I'm going to do type of props.onRemove triple equals function followed by double ampersand and then our call to onRemove. This will just make sure that this fails gracefully if onRemove isn't passed or something is passed in that's not a function.

[5:45] If we go back to the browser, we can delete one more item just to verify that that works. Everything is still working as expected.