Up till now in all of the apps that we've created where we've used state, I've actually tried to keep 1
the component tree very simple. And in fact all that we have is just a single app component where everything 2
is being rendered. 3
Now we know from previous lessons that this is not how a React app will end up looking like. We would 4
probably want to separate out this app into separate components. 5
So we'd probably have a component for the heading area, 6
we'd probably have one for this div with the input and the button, we'd probably then have each of these 7
s being a separate component being rendered. 8
So let's see how this would pan out. 9
Go ahead and fork the starting sandbox and then go into your components folder and create a new component 10
called ToDoItem. 11
jsx. 12
Now inside this to do item, I want you to take out the and create a separate component. 13
The behaviour that you're looking for afterwards is the ability to add new items to the list like so. 14
Pause the video and I'll wait for you. 15
All right. 16
So first we're going to have to import React from react. Next 17
we're going to create our function which is going to be called ToDoItem just like the name of the 18
file 19
and here we're going to return an which is going to contain some text from the to do list. 20
Finally we're going to export this as the default and the name is ToDoItem. 21
Now we can go into our App.jsx and import are ToDoItem from ./ToDoItem. 22
Alright so now we can use this component instead of this when we want to render it. So we can add 23
a self closing tag which is called ToDoItem, and then in order to display a different ToDoItem each 24
time we map through our items array, we're going to pass this ToDoItem which is the text, 25
this should display as a property. So we'll call it text maybe and set it equal ToDoItem. 26
So now we can receive this text inside our ToDoItem as one of the props and instead of having this 27
hardcoded text, I'm gonna open up a set of curly braces and happened to props.text. 28
So now if we refresh our app and add buy milk, click add, you'll see it has exactly the same functionality 29
as before but this time we've now got a separate component. And that's the solution to the challenge. 30
This was a mini review of something we've covered already namely splitting our app into different components. 31
But now that we know all about state, we need to start thinking about how our state is handled across 32
the app and ToDoItem components. And this ToDoItem component 33
at the moment is what you would call a stateless component because it's not trying to change itself 34
or anything else. 35
All it's doing is receiving some read only properties and it just displays them inside. 36
Remember that props are read only so you can't for example say inside here. 37
Let's say if we had a div and inside the div we had an inputs and let's say that this input had an on 38
Change and when it gets called then we're going to trigger some code and we tried to modify the props 39
and we say props equals new thing. 40
This unfortunately does not work as I'll show you. 41
So I add buy milk, 42
so there's a new input that's been added as well because I'm adding a new to do item. 43
And ideally when I start typing, it should update our props but it does not because props is read only. 44
So we can't modify our props but what we can do though is we can have state inside these components. 45
Here's a challenge for you. 46
See if you can change the code just inside this ToDoItem file so that when you add a new to-do item 47
and you click on it, it crosses out the item and then when you click on it again it uncrosses it out. 48
So you've already seen what linethrough does when you change the text decoration to this particular 49
value. It basically just crosses out the text. And you've learnt enough about state to be able to figure 50
out when this or this div gets clicked, how it should change this text styling. So you'll need to 51
think about inline styling, ternary operators, conditional rendering, event handling and using state to 52
complete this challenge. Pause the video now and give that a go. 53
All right. 54
So the first thing we need to do is test to see if we can change this using inline styling. 55
So let's tap into the style property and inside the style property 56
let's add some code. 57
So we're going to say that if we wanted the style to just be applied, then we could simply add a Javascript 58
object as the code and the property was called textDecoration which of course has to be camel case 59
and then the value was called line-through, 60
and this is going to be a string. 61
So let's add a new item and see if that works. 62
So brilliant. 63
That's now working. 64
But we need this to be rendered depending on a condition. And that condition is whether if the user has 65
clicked on this to do item. So you can either add a onClick to the list item or to the div. 66
It really doesn't actually matter. 67
So I'm going to add it to the div just to keep this code on one page and I'm gonna get hold of the on 68
Click. 69
And when that happens I'm going to call a function. And the function is going to be called handleClick. 70
And that's what I'm going to trigger over here as well when this div gets clicked. 71
I want to set a boolean maybe to true or to false depending on whether if this text decoration should 72
be applied. Let's say that I had a constant called isDone and this isDone property is either true or 73
false depending on whether if the user clicked on this div. And to set this isDone property, I'm going 74
to have a function called setIsDone and I'm of course going to need to use state to achieve this. 75
Now the initial value of isDone is going to be false because all items should start out being not done 76
with no line through. 77
But then if the user clicks on this div, then I should be able to handle this click and call setIsDone. 78
And I want to set it to the opposite of what it currently is. 79
So I can use an arrow statement to get the previous value and then I can return the opposite of the 80
previous value. 81
You can of course use a bunch of IF statements If previous value is true then return false, else return 82
true. 83
But actually a much easier way of doing this which you probably already know is to simply return the 84
opposite of previous value like so. 85
So this way if previous value is true then this is going to equal false, if it equals false then it's 86
going to equal again the opposite. 87
So now that we're handling click and we have our click added to our div, all we have to do now is to 88
use this isDone to conditionally render this textDecoration. 89
I'm going to say that this textDecoration if isDone is true ? should be the line-through 90
but else the textDecoration should be set to none. 91
This way effectively when this isDone is true, we're going to apply line-through text decoration, 92
when this is false we're going to change the textDecoration back to none even if this was already applied. 93
Let's go ahead and check our app. 94
So add a new item, click add. Click on it, it gets striked through. Click on it again the text decoration 95
becomes none. 96
So this is all very well and good and we've now got state being managed inside our ToDoItem. 97
But notice how the state is kind of localized to this item. 98
It's not going anywhere else. 99
What if we wanted to change the state of a parent component? Let's say that when this div gets clicked 100
on, instead of just striking out the text, 101
what if we wanted to delete it from our items array? How do we reach up from our ToDoItem into its parent 102
the app component and somehow change this items array? So first I'm going to delete that isDone component 103
and then I'm going to delete everything that's inside handleClick. I'm also going to delete my inline 104
style as well. And I'm going to delete the useState as well 105
so we're now back to a very simple ToDoItem. 106
Now we're ready to tackle the next stage. 107
How do we manage to add an item and when we click on it instead of striking it through, we're changing 108
its styling? 109
I actually want to affect the entire array and this is the desired behavior. 110
I want to click on it and it should delete from my array. 111
So if I had multiple items, say buy milk, buy eggs, buy bacon and I decide to check off buy eggs, that should 112
be the only one that's removed from my array. 113
Let's see how we can tackle this. 114
The first thing to realize is that when we pass over props to our child components, we can also pass 115
over functions which gets called by our child component. 116
For example, when we create each to do item in addition to the text property, we can add another property. 117
So we could call it anything we want, 118
so say onChecked maybe. And we can set this equal to a function inside our app component. 119
So let's say that we wanted to create a function called deleteItem and this was the function that 120
we would pass to our child ToDoItem. 121
So we'll call it deleteItem and then inside our ToDoItem we'll now be able to trigger, instead of handle 122
Click which lives inside the ToDoItem, 123
we can now trigger props.onChecked. And this means that it'll be able to trigger deleteItem inside 124
all app component right here. 125
So let's go ahead and just log this event, 126
like so. 127
So now when I add a buy milk, click Add, and when I click on it you can see that in my console I'm getting 128
this message being sent. 129
So this entire function is being packaged up and sent over to the to-do item under the prop onChecked. 130
And then that prop is only triggered when this div detects a click. 131
And now I can address what should happen when that item wants to delete itself from the array. If I wanted 132
to delete an item from my items array, I'm probably going to need to call setItems. 133
And inside this set items, I'll need to get hold of the previous items and open up a set of curly braces 134
for my arrow function. 135
Now how can i delete the item that requested the deletion? 136
Well one of the things I could do is when I call this onChecked, I could pass over something that identifies 137
this particular ToDoItem from all of the other ones that gets rendered. And I can do that by adding 138
an id. 139
So inside my to do list item, 140
firstly we need to remember that when we map through arrays and create components we should always have 141
a key. 142
And I'm going to set this key to the index of the ToDoItem from the items array. And this map function 143
actually gives me a really easy way of accessing that. 144
Notice how in this callback function the first item I provide is the value, 145
so the value of each item from the array. 146
So in this case its the do item that I've already tapped into. 147
But if I add a comma and I add another variable name then I can tap into the index of this item in the 148
items array. 149
So let's try doing that. 150
Remember from our lesson on arrays that if you only have one parameter, you can leave out the parentheses. 151
But if you want to have more than one parameter then you'll need the parentheses and add a comma to 152
separate each of those items. 153
So the second one I'm going to call index. 154
This is going to be the index of the current ToDoItem that's being looped through the items array 155
and I'm going to use the index as the key. Now 156
in an ideal world, React advises us to not use the index for the key and instead use some sort of unique 157
identifying string. 158
For example you could use a package such as uuid which basically just allows you to generate a u 159
uid by installing it as a package. 160
So super simple. But in our case we're going to leave it as the index because we want to use this index 161
value and we want to use it to pass over to our ToDoItem as an id. 162
So now that means our ToDoItem should be able to receive the id in the props. 163
Let's see if that is indeed the case. 164
So let's add a new item and then go to our components click on this item and you can see it's got the 165
id being passed over as 0 because it's the first item in the array. 166
This one has a id of one because it's the second item. 167
They both get some text as the props and they also get this method onChecked which corresponds to the 168
deleteItem function. 169
So you might think that given that we have access to the id now inside this function, I could just 170
pass it back over as props.id and that way my onChecked function will be able to receive the id 171
right here and then I could probably just log it. 172
Now I'm going to show you what actually happens instead. 173
If I go ahead and keep the console open and I add a new item, you'll see that immediately this console 174
log triggers and it prints out the id of the item. 175
So what's going on here? 176
Now remember that way back and we talked about JavaScript functions and passing around functions. 177
We said that whenever you have a set of parentheses and something inside it, then you're going to be 178
calling the function rather than passing the function. 179
So we can't actually pass our props.id like this. 180
This is just going to call it immediately the moment that this ToDoItem gets rendered. 181
Instead what we can do in our onClick is to create a function and this function will only get called 182
when the onClick gets triggered and it's only in this moment that we actually want to tap into props 183
.onChecked and then passing in the props.id. This way when the ToDoItem gets rendered, 184
this function is not going to be called until this div detects a click. 185
Let me reiterate this. Previously when we click the Add button and rendered the ToDoItem component 186
it triggered props.onChecked immediately. But now props.onChecked will only be called the moment 187
the item is actually clicked. 188
Now if I add an item click Add, notice that nothing gets printed until I click on this ToDoItem. Now 189
that we know we can get hold of the id of the ToDoItem inside this delete function. 190
Well now we can activate our setItems and we can use what we've learned before to change our items 191
array. 192
More specifically we're going to use the filter function that we learned before and filter through all 193
of the previous items in our items array and get rid of the ones which match this id. So inside our 194
setItems 195
we're going to return a array and that array is going to be constructed from our previous items array. 196
But it's going to be filtered so that we get rid of the item that we don't want, 197
so the one that has this id that was passed over. Remember that the filter function again expects 198
a function itself. 199
Let's go ahead and add a arrow function and inside this filter function, we have access to two properties. 200
The first one is the actual element that's being looped through in the array 201
and the second one is the index of the current element. 202
So we can tap in to each item as we're looping through previous items and we can also tap into the index 203
of that item. 204
Now we're going to go through our array of previous items, look through each item and get the index of 205
each item and we're going to return an output, a final array, that is going to return all the items where 206
the index is not equal to the id being passed over. 207
So let's go ahead and hit save and check this out inside our new window. 208
So I've got my React dev tool open here as well and I'm going to add a new item. 209
So this went into the array with the index of 0, so it's the first item. And the id got passed over to 210
the ToDoItem component as a prop. 211
And in addition, there's another prop, onChecked, which passed over the deleteItem function that we were 212
editing here. And I've set up the code such that when I click on this ToDoItem, it will trigger delete 213
Item and it will pass over the id as the input. Then we get to receive that id here which remember 214
equates to the index of the item that we're trying to delete. 215
And then we call setItems to update our list of items and we filter it so that we only end up with 216
the ones where the index doesn't match this id, 217
so everything other than the one that's being clicked. 218
So let's add a couple more. 219
And if I go ahead and click on buy eggs, then the id is going to be 1, that's going to be passed to 220
deleteItem. 221
It's going to loop through my array of three items and filter so that we only end up with items where 222
the index does not equal one. 223
So that's this one and this one. 224
So that means if I click here, you can see eggs disappears 225
but we end up with still the remainder of our array that's been filtered using this rule. 226
This really is getting into a lot of functional programming that you'll see a lot of 227
in React where we're passing one function into another function inside another function inside another 228
function inside another function. 229
It does take some time to be able to wrap your head around this and I strongly encourage you to have 230
a go at playing around with this code, trying to filter on some sort of different condition maybe changing 231
the items so that instead of being an array, it's an array of objects. 232
Just have a mess around with it until you get comfortable with it. 233
And it always takes a lot of time to mess around with these things especially something as complex as 234
this to feel comfortable. And the idea is that I'm not expecting you to remember how to do this. Just 235
so that you understand what's going on, so you can look it up the next time.