WEBVTT 0 00:00.300 --> 00:08.160 Alright guys it's time to apply everything that we've learned so far to our keeper app project and 1 00:08.220 --> 00:11.080 make it actually have some functionality. 2 00:11.310 --> 00:17.260 If you head over to the course resources and fork your own copy of the keeper part 3 3 00:17.400 --> 00:23.910 starting project, you'll notice that the challenge instructions are very simple. 4 00:23.910 --> 00:29.750 All we want to do is to be able to add notes and delete notes from our keeper app. 5 00:29.760 --> 00:36.550 This is the final functionality that you're looking for. When we add a note with a new title 6 00:36.780 --> 00:46.890 and when we click the Add button it clears those inputs and pushes a new note into our array of notes. 7 00:46.890 --> 00:55.020 So if we added another, then you can see they will just keep getting added alongside each other. 8 00:55.050 --> 01:02.310 The second part of the challenge is to be able to click on the delete button and for that note to be 9 01:02.370 --> 01:05.590 removed from the stack of notes here. 10 01:05.610 --> 01:11.700 Now even though it sounds simple, there's only two steps each of these steps will involve quite a few 11 01:11.700 --> 01:14.570 things that you've learned along the way. 12 01:14.760 --> 01:20.430 If you want to get started with the challenge and you think you can tackle it already, then go ahead 13 01:20.430 --> 01:22.590 and pause the video and give it a go 14 01:22.590 --> 01:28.710 now. If you want to watch him break down the challenge into smaller pieces so that you can tackle it 15 01:28.740 --> 01:36.630 one by one which is obviously a lot easier, then feel free to continue listening. 16 01:36.830 --> 01:37.140 All right. 17 01:37.170 --> 01:44.220 So the first part of implementing the add Note functionality is probably going to involve some sort of 18 01:44.280 --> 01:45.880 state management right? 19 01:45.930 --> 01:55.650 We're probably going to have to create a constant that keeps track of the title and content. 20 01:55.650 --> 02:03.240 In addition, when the new note whether it's title or content gets created, we're probably going to want to 21 02:03.330 --> 02:12.810 pass the new note back to the app component. Ad then maybe in the app component we'll have some sort 22 02:12.810 --> 02:22.140 of array right? Which we can add the new note to an array and this array is probably going to be something 23 02:22.140 --> 02:33.010 with states which you can set state and you can read it. And then we can take that array and render a 24 02:33.210 --> 02:40.440 separate Note components for each item in the array. 25 02:41.180 --> 02:47.330 And these sub steps should together create the implement add Note functionality. 26 02:47.330 --> 02:55.190 Now the second step where we want to delete notes, we're probably going to need some sort of way of getting 27 02:55.250 --> 03:07.670 a callback from the Note component that's going to trigger a delete function. The delete function is 28 03:07.670 --> 03:11.060 probably going to live in the App.jsx. 29 03:11.400 --> 03:21.470 And in this case, we're probably going to want to use the filter function to filter out the item that 30 03:21.770 --> 03:25.600 needs deletion. 31 03:25.650 --> 03:34.530 So you're going to have to think about how to pass an id over to the Note component so that when you 32 03:34.530 --> 03:43.380 trigger the delete function, you can pass it back to the app component in order to identify the Note 33 03:43.440 --> 03:47.140 that needs to be removed from the array. 34 03:47.190 --> 03:55.050 So this is the broken down version of the challenge and I recommend giving it a go and seeing how you 35 03:55.050 --> 03:57.120 get on. If you get stuck 36 03:57.150 --> 04:04.170 then come back and watch me go through the solution step by step and then maybe that'll help you get 37 04:04.200 --> 04:09.670 unstuck and you can continue with the rest of the steps of the challenge. 38 04:09.690 --> 04:10.000 All right. 39 04:10.020 --> 04:13.140 So pause the video now and try to complete this challenge. 40 04:17.920 --> 04:18.380 All right. 41 04:18.410 --> 04:24.530 The first thing we're going to address is implementing the add note functionality and the sub 42 04:24.530 --> 04:31.160 step of this was to create a constant that keeps track of the title and the content so that we can save 43 04:31.220 --> 04:33.840 what is typed into these two inputs. 44 04:34.280 --> 04:39.860 So I'm going to open up my components folder and go to this CreateArea component. 45 04:39.860 --> 04:49.490 Now here you'll notice that there is a very simple form with an input, a text area which acts pretty 46 04:49.490 --> 04:51.220 much like an input, 47 04:51.350 --> 04:55.710 and finally a button that just reads add. 48 04:55.730 --> 05:02.060 So we've only got three components and we want to be able to save what's typed into this input under 49 05:02.060 --> 05:03.920 something called title 50 05:03.920 --> 05:06.880 and this one under something called content. 51 05:06.950 --> 05:15.980 Let's go ahead and create our stateful constants and I'm going to create a object in this case that 52 05:15.980 --> 05:18.880 contains both the title and the content. 53 05:19.040 --> 05:24.290 You could of course create separate constants, one for the title and one for the content, 54 05:24.440 --> 05:29.210 but it means you'll have to write more lines of code and do a little bit more work. 55 05:29.240 --> 05:34.790 I'm going to create a constant called note and I'll create a function called setNote which is going to 56 05:34.870 --> 05:35.780 update it. 57 05:36.080 --> 05:43.490 And I'm of course going to need to use state. The initial state is just going to be an object with a 58 05:43.490 --> 05:50.750 title key that's set to an empty string and a content key that's also set to an empty string. 59 05:50.750 --> 05:56.460 Now I can use this note inside my input and my text area. 60 05:56.690 --> 06:07.850 So I'm going to have two values and this one is going to be equal to note.title and this one will 61 06:07.850 --> 06:15.050 be note.content. Now that we've got our inputs controlled, 62 06:15.050 --> 06:19.880 the next step is to update them when they get changed. 63 06:20.000 --> 06:29.490 So inside the input and the textarea I'm going to add a onChange. And inside both of these onChange 64 06:29.570 --> 06:40.220 I'm going to call a function which I'll call handleChange. And then we can pass it in as the onChange 65 06:40.310 --> 06:42.970 for both of these like 66 06:42.980 --> 06:49.310 so. Now handleChange is going to receive an event when it gets triggered 67 06:49.310 --> 06:57.590 and in this case we're going to destructure the event so that we get hold of the event.target. 68 06:57.590 --> 06:59.260 name and .value. 69 06:59.570 --> 07:08.930 So we're going to create a new destructured object using the const keyword and this destructured object 70 07:08.960 --> 07:16.890 is going to tap into the event.target.name and event dot tevent.target.value like so. 71 07:16.900 --> 07:26.750 So now we can use this name and value as separate constants and I'm going to add to my note by calling 72 07:26.780 --> 07:35.900 setNote. Now setNote is going to be able to receive the previous note and I'm going to use that to 73 07:35.960 --> 07:38.290 add to the existing note. 74 07:38.780 --> 07:45.980 So in my setote I'm going to return a new object with the previous note 75 07:46.430 --> 07:56.190 and in addition I'm going to add the new name and value. And remember that this syntax simply turns this 76 07:56.250 --> 08:05.040 name key from just the string name for the key to the actual value of this name constant. 77 08:05.220 --> 08:11.760 And this is of course the spread operator which is going to spread all of the key value pairs that is 78 08:11.760 --> 08:14.200 currently in existence in our note. 79 08:14.670 --> 08:21.550 And it's going to add it to this final object. This way when we start typing 80 08:21.660 --> 08:26.220 then you'll see it being saved inside our state. 81 08:26.220 --> 08:31.740 So if I go ahead and click on my create area, extend my state object, 82 08:31.740 --> 08:37.670 this is a title and this is the content. 83 08:37.710 --> 08:43.410 So if you're not seeing this then there might be something that you're missing in your code. 84 08:43.410 --> 08:46.740 So this is the first step. 85 08:46.860 --> 08:54.370 We've created a constant that keeps track of the title and the content. The next step is to somehow pass 86 08:54.400 --> 09:03.130 this newly created note back to the app. So we know that we've got this note saved in this constant and 87 09:03.250 --> 09:10.210 we know that we have a button called add which is supposed to trigger this adding of the note functionality. 88 09:10.870 --> 09:18.520 So why don't we go ahead and add a onClick to this button and try to get this note that's been created 89 09:18.520 --> 09:22.430 here passed back to the App.jsx? 90 09:22.510 --> 09:29.620 So when this button gets clicked, we're going to pass it a function that should be triggered. 91 09:29.650 --> 09:37.120 So in this case I'm just going to call that function submitNote and this submitNote is going to be 92 09:37.120 --> 09:40.040 triggered here. 93 09:40.110 --> 09:45.340 Now remember what we said about forms and buttons inside forms. 94 09:45.540 --> 09:53.010 Whenever you click on a button inside a form, the default behavior is to refresh the page. And you can 95 09:53.010 --> 10:01.550 see that if I type something in here and I click add, you can see the entire screen flashes and refreshes. 96 10:01.560 --> 10:10.050 So the first thing we need to do is to prevent this. So we can pass the event to this submitNote and 97 10:10.050 --> 10:19.710 the event is triggered by the onClick and we can call event.preventDefault. 98 10:20.050 --> 10:28.390 This way you'll notice if I go back over here and I type something in here and I click Add, notice how 99 10:28.390 --> 10:35.680 the screen is not flashing anymore. So I'm preventing this entire reloading of the page by using the 100 10:35.680 --> 10:39.880 event.preventDefault which we saw in previous lessons. 101 10:39.880 --> 10:47.350 The next thing to do is to somehow trigger a function that can pass this note back over to the App, 102 10:47.350 --> 10:49.750 jsx. And to do that 103 10:49.840 --> 10:54.220 we of course have to pass in a function as a prop. 104 10:54.490 --> 11:02.830 So let's go ahead and create a function called addNote and this addNote is going to receive a note 105 11:02.890 --> 11:06.970 object and it's going to do something with that note object. 106 11:06.970 --> 11:14.140 So that addNote is going to be added as a value to one of the props for the create area and you can 107 11:14.140 --> 11:22.630 call it anything you want but I'm going to call it onAdd and I'm going to set it equal addNote . 108 11:22.630 --> 11:30.640 So now if we take a look at our components when we head over to create area it gets a prop under the 109 11:30.700 --> 11:38.090 property name of onAdd and that is going to be equal to the addNote that comes from the App. 110 11:38.120 --> 11:38.970 js. 111 11:39.070 --> 11:49.420 Now we are going to trigger this addNote by getting hold of the props that gets passed over and especially 112 11:49.540 --> 11:58.570 the props.onAdd. And calling this onAdd from the props is going to be equivalent to calling the 113 11:58.660 --> 12:05.560 addNote from the App.jsx. And in order for us to be able to add the note we have to pass it back 114 12:05.620 --> 12:06.710 as an input. 115 12:06.790 --> 12:16.180 So in the onAdd we're going to pass over the current created note, like so. Now we're pretty much done 116 12:16.240 --> 12:21.650 with our second step which is passing the new note back to the app. 117 12:21.880 --> 12:29.200 And if you go into this addNote and go ahead and try to log the note just to check that it works, then 118 12:29.260 --> 12:35.260 you can see that if I go ahead and just type some gibberish in here and click add and I look in 119 12:35.260 --> 12:41.770 my console, then you can see that the only place where I'm logging it is inside my app component inside 120 12:41.770 --> 12:47.610 the addNote function and I'm now able to receive this note and be able to log it right here. 121 12:48.130 --> 12:50.550 So all is good in the world. 122 12:50.620 --> 12:55.970 The next sub step is to add this new note to an array. 123 12:56.020 --> 13:03.760 This is going to happen in our App.jsx and that array is going to need state because it's going 124 13:03.760 --> 13:04.890 to be changed. 125 13:05.020 --> 13:13.120 Let's create a constant and I'm going to call it notes to represent the plural nature of our array of 126 13:13.120 --> 13:18.940 notes. And then I'm going to create a function called setNotes which is going to help me update this 127 13:18.940 --> 13:20.620 notes array. 128 13:20.890 --> 13:27.850 Then we of course have to use state and import state and the initial value for my notes is just going 129 13:27.850 --> 13:33.880 to be an empty array. Instead of having this sort of sample note up here, 130 13:33.910 --> 13:41.280 I'm just going to start it out with no notes and only when the user actually adds one does it get populated. 131 13:41.670 --> 13:47.940 Now that we have this notes array and we're receiving the new notes via this function, then we can think 132 13:47.940 --> 13:55.770 about how we can add to this notes array. So we can probably use these setNotes function and inside 133 13:55.770 --> 14:02.700 this function we're going to get a hold of the previous notes or whatever is the previous value of this 134 14:02.760 --> 14:10.590 notes array which at the moment it's empty but soon it's going to be populated with notes objects. And 135 14:10.590 --> 14:17.370 to add to this notes array we're going to use the spread operator again so that we get hold of all of 136 14:17.370 --> 14:26.050 the previous notes and then add the new note at the end. If you find it easier it might be worth changing 137 14:26.050 --> 14:28.740 this property to new note 138 14:28.750 --> 14:35.200 and remember you can call it anything you want here as long as you know that this value is being passed 139 14:35.200 --> 14:37.900 over from here. 140 14:37.900 --> 14:43.540 So now that we've created this array we're going to have to return it to get rid of that little squiggly 141 14:43.540 --> 14:44.220 line 142 14:44.350 --> 14:51.250 so that setNotes actually gets a array being returned and that is going to be set as the new notes 143 14:51.310 --> 14:52.910 array. 144 14:53.070 --> 14:59.550 So now all we have to do is the last step which is to take the array and render separate note components 145 14:59.550 --> 15:02.160 for each of the items in the array. 146 15:02.160 --> 15:07.220 Notice how we've got the squiggly line under notes and it's because we're not using it anywhere. 147 15:07.320 --> 15:09.390 So let's do that right here. 148 15:09.930 --> 15:17.010 So at the moment I've got a sample note being created but instead of creating the sample note, I want 149 15:17.010 --> 15:25.560 to use my notes array and map through it to render a different note component for each item inside the 150 15:25.560 --> 15:26.660 array. 151 15:26.670 --> 15:32.330 My map is going to take a function and I'm just going to create an arrow function in this case. 152 15:32.970 --> 15:40.590 And for each of the note items inside this notes array remember that the map function is going to loop 153 15:40.590 --> 15:48.650 through this notes array and get hold of each of the note items inside and execute this function. 154 15:48.810 --> 15:57.840 Then I'm going to return a new note component and this note component is going to need some properties 155 15:57.840 --> 15:59.130 passed over. 156 15:59.190 --> 16:06.120 So one of these is going to be the title which is going to be set as noteItem.title. 157 16:06.120 --> 16:12.080 The next is the content which is going to be the noteItem.content. 158 16:12.330 --> 16:18.030 And now in order for this code to be recognized as Javascript I'm going to go ahead and select all of 159 16:18.030 --> 16:23.080 it and add the curly braces around it so that my errors go away. 160 16:23.970 --> 16:32.330 So now I can replace this static note with the notes that are created from my create note component. 161 16:32.700 --> 16:35.280 And if I go ahead and try it out 162 16:35.340 --> 16:36.870 so I've got a title, 163 16:36.870 --> 16:44.050 'This is a new note and here is some content'. 164 16:45.440 --> 16:56.280 I click Add and it gets added down in here. My array gets a new object with some title and some content 165 16:56.670 --> 17:03.450 like so. That's step one completed, implementing the add note functionality. 166 17:03.570 --> 17:11.400 Step two is to implement the delete note functionality and the first step of doing this involves creating 167 17:11.400 --> 17:19.740 a callback from the note component to trigger a delete function. Inside our note component we have a delete 168 17:19.890 --> 17:21.090 button. 169 17:21.090 --> 17:27.720 And what we want to do is to be able to somehow get this delete button to trigger a function in the 170 17:27.720 --> 17:33.780 App.jsx. And to do this we're going to have to rely on our props. 171 17:33.780 --> 17:37.490 We're going to have to tap into the onClick attribute here. 172 17:37.770 --> 17:46.260 And when the button gets clicked we're going to handle the click and I'm going to create a function 173 17:46.350 --> 17:49.220 up here which is going to be called handleClick. 174 17:49.380 --> 17:55.900 Now you can of course have this as an inline anonymous arrow function if you want, but I'm just gonna 175 17:55.920 --> 17:56.810 keep it separate 176 17:56.820 --> 18:02.550 so it's a little bit easier to see what's going on. Now inside this handleClick is where we're going 177 18:02.550 --> 18:08.060 to trigger a function that gets passed over from the props that will delete this note from the array. 178 18:09.220 --> 18:15.480 So back in our app component, we're going to create a another function below the addNote which is going 179 18:15.480 --> 18:24.510 to be deleteNote. And we're going to need the id of the note that needs to be deleted. 180 18:24.870 --> 18:31.770 And then this function as it is is going to be passed over to each of these notes that gets rendered 181 18:32.220 --> 18:34.270 as a property. 182 18:34.380 --> 18:39.220 So I'm going to call it onDelete and I'm going to add it inside here 183 18:39.300 --> 18:44.300 deleteNote as the function that's going to be passed over. 184 18:44.310 --> 18:51.370 So now if we go ahead and create a note and add it when we click on the note component in our React dev tools, 185 18:51.510 --> 18:57.390 you can see that it's got a prop called onDelete that we can tap into which is going to trigger the delete 186 18:57.450 --> 19:03.970 Note function. Inside our Note.jsx, inside the handleClick function 187 19:03.970 --> 19:12.220 we're going to tap into props.onDelete and that's going to trigger this function right here. 188 19:12.220 --> 19:22.580 So let's go ahead and log that this delete was triggered. And now if I go ahead and create a new note, 189 19:22.940 --> 19:30.200 click Add and click delete, then you can see that 'Delete was triggered' gets printed in the console. 190 19:30.260 --> 19:37.190 So we're now able to trigger our deleteNote function in our app component but instead of console logging 191 19:37.310 --> 19:45.890 we're probably going to want to somehow remove the note from our notes array. So we can do that using 192 19:45.950 --> 19:55.900 setNotes. And inside setNotes we can get hold of the previous notes just as we did up here. 193 19:56.560 --> 20:02.680 So let's go ahead and create a new arrow function which uses the previous notes. 194 20:02.710 --> 20:10.420 Now once we have access to all of the previous notes array, well then we can loop through it using the 195 20:10.480 --> 20:11.800 filter function. 196 20:12.190 --> 20:18.510 And remember that the filter function takes a function that accepts up to three arguments. 197 20:18.640 --> 20:22.600 We can go ahead and add a arrow function inside. 198 20:22.600 --> 20:27.800 So this is our function that's being passed to our filter function 199 20:28.000 --> 20:30.370 and it can take up to three arguments. 200 20:30.580 --> 20:34.680 The first one is the value that we're currently looping through in the array. 201 20:35.050 --> 20:37.300 So that's going to be the noteItem. 202 20:37.960 --> 20:42.370 And the second one is the index of this item. 203 20:42.400 --> 20:48.010 So that's going to be really useful very shortly when we're going to be checking to see which ones we 204 20:48.010 --> 20:51.270 want to filter out. Inside our filter function 205 20:51.280 --> 21:00.550 we're going to return all of the notes where the index does not equal the id of the note that needs 206 21:00.550 --> 21:02.100 to be deleted. 207 21:02.110 --> 21:08.290 So this way we should end up with an array that contains everything in the previous notes other than 208 21:08.290 --> 21:17.620 the ones where the index matches the id of the note to be deleted. And we're going to need to return 209 21:17.620 --> 21:28.780 it in order to set it as the new notes array. The last thing we need to do is somehow pass over the index 210 21:29.080 --> 21:34.420 of the note that is being deleted through this deleteNote function. 211 21:34.630 --> 21:39.610 Now at the moment when we create our note, we actually don't pass over 212 21:39.610 --> 21:47.590 firstly the required key attribute but also some sort of id attribute that we can then pass back when 213 21:47.590 --> 21:50.860 we want to delete that note. 214 21:51.160 --> 21:57.790 This is the last step in the challenge. Somehow passing id over to the note component and pass it back 215 21:57.790 --> 22:00.100 to the app when deleting. 216 22:00.100 --> 22:02.110 How do we create this id? 217 22:02.380 --> 22:09.730 We can of course create the id using a uuid or some sort of timestamp that's unique. 218 22:09.730 --> 22:16.510 But the simplest way in this case is to know that map works very similarly to filter. In that in the 219 22:16.510 --> 22:17.490 callback function, 220 22:17.500 --> 22:23.400 you can have the value but you can also get the index of the current item that's being looped through. 221 22:23.830 --> 22:29.680 Let's go ahead and add a set of parentheses around our note item because we're going to add a second 222 22:29.680 --> 22:39.670 parameter which is the index. And we're going to use that index both as the key and also as the id. 223 22:41.040 --> 22:43.770 When each of these note components are rendered 224 22:43.770 --> 22:51.450 we're going to be passing over the index of the noteItem from the notes array as the id which we 225 22:51.450 --> 22:59.520 can then pick up inside our note component so that when we call props.onDelete we pass over that 226 22:59.520 --> 23:08.280 prop id. So we can happen to props.id and send it back when we trigger the onDelete. 227 23:08.280 --> 23:11.570 So now if we hit save and we check it out, 228 23:11.640 --> 23:23.400 so let's create a new title 'A new note' and click add, and then if we click delete it will disappear from 229 23:23.400 --> 23:30.790 the array. Now the last a little bug that you might want to address is when you click on the add button 230 23:31.300 --> 23:34.810 the title and the content should really clear itself. 231 23:34.840 --> 23:39.260 So how can we do this? Well inside our createArea 232 23:39.440 --> 23:49.150 once we've submitted the note, we can also clear out this note object. We can call setNote and we can 233 23:49.150 --> 23:54.010 pass it the same object that we had to begin with. 234 23:54.010 --> 24:01.990 This way when we call submitNote, the note gets added over to our App.jsx and we set the note state 235 24:02.230 --> 24:10.450 to both an empty title and an empty content. And because our input has the value of note.title, our text 236 24:10.450 --> 24:15.990 Area has a value of note.content, it should reflect the state of the notes. 237 24:16.060 --> 24:17.110 Let's try it out. 238 24:17.110 --> 24:23.050 If we type something in title, type something in the notes, click add and they both disappear and go back 239 24:23.050 --> 24:25.610 to the original placeholder text. 240 24:25.720 --> 24:31.690 So this is the solution to the full challenge and it's a really long one and it's a really hard one 241 24:32.020 --> 24:35.710 but there isn't anything that you haven't learnt in previous lessons. 242 24:35.710 --> 24:41.620 So if you got stuck in any of these places then be sure to head back to the relevant section where we 243 24:41.620 --> 24:46.870 covered it so that you can get a little bit more familiar with each of these steps so that you could 244 24:46.870 --> 24:50.510 complete this challenge comfortably by yourself. 245 24:50.590 --> 24:56.530 Now all we have left is some tidying up and in the next lesson I want to show you how you can add some 246 24:56.530 --> 25:03.530 dependencies using React to get hold of some components that we can use in our React apps. 247 25:03.550 --> 25:05.790 So for all of that and more, I'll see you there.