WEBVTT
0
00:00.480 --> 00:07.020
Up till now in all of the apps that we've created where we've used state, I've actually tried to keep
1
00:07.020 --> 00:14.910
the component tree very simple. And in fact all that we have is just a single app component where everything
2
00:14.910 --> 00:16.360
is being rendered.
3
00:16.410 --> 00:24.000
Now we know from previous lessons that this is not how a React app will end up looking like. We would
4
00:24.000 --> 00:28.350
probably want to separate out this app into separate components.
5
00:28.350 --> 00:31.650
So we'd probably have a component for the heading area,
6
00:31.740 --> 00:38.880
we'd probably have one for this div with the input and the button, we'd probably then have each of these
7
00:38.970 --> 00:42.930
s being a separate component being rendered.
8
00:43.000 --> 00:45.600
So let's see how this would pan out.
9
00:45.630 --> 00:54.900
Go ahead and fork the starting sandbox and then go into your components folder and create a new component
10
00:55.170 --> 00:58.200
called ToDoItem.
11
00:58.460 --> 00:59.900
jsx.
12
01:00.190 --> 01:08.640
Now inside this to do item, I want you to take out the and create a separate component.
13
01:08.970 --> 01:14.280
The behaviour that you're looking for afterwards is the ability to add new items to the list like so.
14
01:15.320 --> 01:17.250
Pause the video and I'll wait for you.
15
01:20.260 --> 01:20.640
All right.
16
01:20.670 --> 01:26.130
So first we're going to have to import React from react. Next
17
01:26.160 --> 01:31.740
we're going to create our function which is going to be called ToDoItem just like the name of the
18
01:31.740 --> 01:32.630
file
19
01:32.760 --> 01:40.580
and here we're going to return an which is going to contain some text from the to do list.
20
01:40.590 --> 01:47.460
Finally we're going to export this as the default and the name is ToDoItem.
21
01:47.460 --> 01:57.810
Now we can go into our App.jsx and import are ToDoItem from ./ToDoItem.
22
01:57.860 --> 02:06.310
Alright so now we can use this component instead of this when we want to render it. So we can add
23
02:06.400 --> 02:13.540
a self closing tag which is called ToDoItem, and then in order to display a different ToDoItem each
24
02:13.540 --> 02:19.720
time we map through our items array, we're going to pass this ToDoItem which is the text,
25
02:19.900 --> 02:28.040
this should display as a property. So we'll call it text maybe and set it equal ToDoItem.
26
02:28.900 --> 02:37.000
So now we can receive this text inside our ToDoItem as one of the props and instead of having this
27
02:37.000 --> 02:44.110
hardcoded text, I'm gonna open up a set of curly braces and happened to props.text.
28
02:44.110 --> 02:52.690
So now if we refresh our app and add buy milk, click add, you'll see it has exactly the same functionality
29
02:52.690 --> 02:59.860
as before but this time we've now got a separate component. And that's the solution to the challenge.
30
02:59.860 --> 03:05.800
This was a mini review of something we've covered already namely splitting our app into different components.
31
03:06.340 --> 03:12.430
But now that we know all about state, we need to start thinking about how our state is handled across
32
03:12.430 --> 03:18.270
the app and ToDoItem components. And this ToDoItem component
33
03:18.280 --> 03:25.960
at the moment is what you would call a stateless component because it's not trying to change itself
34
03:26.050 --> 03:27.490
or anything else.
35
03:27.490 --> 03:33.960
All it's doing is receiving some read only properties and it just displays them inside.
36
03:34.510 --> 03:40.780
Remember that props are read only so you can't for example say inside here.
37
03:40.900 --> 03:50.110
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
03:50.110 --> 03:59.380
Change and when it gets called then we're going to trigger some code and we tried to modify the props
39
03:59.830 --> 04:04.970
and we say props equals new thing.
40
04:05.020 --> 04:08.290
This unfortunately does not work as I'll show you.
41
04:08.290 --> 04:09.690
So I add buy milk,
42
04:09.730 --> 04:14.140
so there's a new input that's been added as well because I'm adding a new to do item.
43
04:14.710 --> 04:22.690
And ideally when I start typing, it should update our props but it does not because props is read only.
44
04:23.620 --> 04:32.140
So we can't modify our props but what we can do though is we can have state inside these components.
45
04:32.140 --> 04:34.300
Here's a challenge for you.
46
04:34.480 --> 04:42.580
See if you can change the code just inside this ToDoItem file so that when you add a new to-do item
47
04:43.090 --> 04:49.540
and you click on it, it crosses out the item and then when you click on it again it uncrosses it out.
48
04:50.170 --> 04:56.320
So you've already seen what linethrough does when you change the text decoration to this particular
49
04:56.320 --> 05:02.950
value. It basically just crosses out the text. And you've learnt enough about state to be able to figure
50
05:02.950 --> 05:11.920
out when this or this div gets clicked, how it should change this text styling. So you'll need to
51
05:11.920 --> 05:20.140
think about inline styling, ternary operators, conditional rendering, event handling and using state to
52
05:20.140 --> 05:23.320
complete this challenge. Pause the video now and give that a go.
53
05:26.490 --> 05:26.940
All right.
54
05:27.060 --> 05:33.800
So the first thing we need to do is test to see if we can change this using inline styling.
55
05:33.840 --> 05:38.270
So let's tap into the style property and inside the style property
56
05:38.340 --> 05:39.590
let's add some code.
57
05:39.930 --> 05:47.190
So we're going to say that if we wanted the style to just be applied, then we could simply add a Javascript
58
05:47.310 --> 05:54.960
object as the code and the property was called textDecoration which of course has to be camel case
59
05:55.500 --> 05:59.660
and then the value was called line-through,
60
05:59.910 --> 06:02.010
and this is going to be a string.
61
06:02.010 --> 06:05.860
So let's add a new item and see if that works.
62
06:05.910 --> 06:06.840
So brilliant.
63
06:06.840 --> 06:08.580
That's now working.
64
06:08.580 --> 06:15.780
But we need this to be rendered depending on a condition. And that condition is whether if the user has
65
06:15.900 --> 06:23.580
clicked on this to do item. So you can either add a onClick to the list item or to the div.
66
06:23.580 --> 06:25.050
It really doesn't actually matter.
67
06:25.410 --> 06:31.740
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
06:31.740 --> 06:32.970
Click.
69
06:32.970 --> 06:40.590
And when that happens I'm going to call a function. And the function is going to be called handleClick.
70
06:42.330 --> 06:50.350
And that's what I'm going to trigger over here as well when this div gets clicked.
71
06:50.350 --> 06:59.260
I want to set a boolean maybe to true or to false depending on whether if this text decoration should
72
06:59.260 --> 07:08.680
be applied. Let's say that I had a constant called isDone and this isDone property is either true or
73
07:08.680 --> 07:16.180
false depending on whether if the user clicked on this div. And to set this isDone property, I'm going
74
07:16.180 --> 07:24.730
to have a function called setIsDone and I'm of course going to need to use state to achieve this.
75
07:25.990 --> 07:32.380
Now the initial value of isDone is going to be false because all items should start out being not done
76
07:32.740 --> 07:34.120
with no line through.
77
07:34.690 --> 07:42.820
But then if the user clicks on this div, then I should be able to handle this click and call setIsDone.
78
07:43.230 --> 07:48.040
And I want to set it to the opposite of what it currently is.
79
07:48.040 --> 07:57.010
So I can use an arrow statement to get the previous value and then I can return the opposite of the
80
07:57.010 --> 07:58.530
previous value.
81
07:58.780 --> 08:06.100
You can of course use a bunch of IF statements If previous value is true then return false, else return
82
08:06.100 --> 08:06.850
true.
83
08:06.850 --> 08:13.120
But actually a much easier way of doing this which you probably already know is to simply return the
84
08:13.240 --> 08:17.240
opposite of previous value like so.
85
08:17.290 --> 08:22.960
So this way if previous value is true then this is going to equal false, if it equals false then it's
86
08:22.960 --> 08:26.140
going to equal again the opposite.
87
08:26.400 --> 08:34.050
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
08:34.050 --> 08:40.150
use this isDone to conditionally render this textDecoration.
89
08:40.230 --> 08:49.080
I'm going to say that this textDecoration if isDone is true ? should be the line-through
90
08:49.620 --> 08:54.760
but else the textDecoration should be set to none.
91
08:54.930 --> 09:01.890
This way effectively when this isDone is true, we're going to apply line-through text decoration,
92
09:02.160 --> 09:09.630
when this is false we're going to change the textDecoration back to none even if this was already applied.
93
09:09.630 --> 09:11.900
Let's go ahead and check our app.
94
09:12.000 --> 09:20.730
So add a new item, click add. Click on it, it gets striked through. Click on it again the text decoration
95
09:20.760 --> 09:23.320
becomes none.
96
09:23.360 --> 09:30.850
So this is all very well and good and we've now got state being managed inside our ToDoItem.
97
09:31.040 --> 09:35.180
But notice how the state is kind of localized to this item.
98
09:35.180 --> 09:37.070
It's not going anywhere else.
99
09:37.220 --> 09:44.420
What if we wanted to change the state of a parent component? Let's say that when this div gets clicked
100
09:44.420 --> 09:47.930
on, instead of just striking out the text,
101
09:47.930 --> 09:58.040
what if we wanted to delete it from our items array? How do we reach up from our ToDoItem into its parent
102
09:58.100 --> 10:09.700
the app component and somehow change this items array? So first I'm going to delete that isDone component
103
10:10.180 --> 10:15.720
and then I'm going to delete everything that's inside handleClick. I'm also going to delete my inline
104
10:15.790 --> 10:19.680
style as well. And I'm going to delete the useState as well
105
10:20.140 --> 10:23.980
so we're now back to a very simple ToDoItem.
106
10:24.190 --> 10:26.730
Now we're ready to tackle the next stage.
107
10:26.830 --> 10:33.580
How do we manage to add an item and when we click on it instead of striking it through, we're changing
108
10:33.580 --> 10:34.780
its styling?
109
10:34.780 --> 10:39.530
I actually want to affect the entire array and this is the desired behavior.
110
10:39.550 --> 10:42.770
I want to click on it and it should delete from my array.
111
10:42.910 --> 10:54.570
So if I had multiple items, say buy milk, buy eggs, buy bacon and I decide to check off buy eggs, that should
112
10:54.570 --> 10:56.970
be the only one that's removed from my array.
113
10:58.050 --> 11:01.440
Let's see how we can tackle this.
114
11:01.710 --> 11:08.970
The first thing to realize is that when we pass over props to our child components, we can also pass
115
11:09.030 --> 11:14.130
over functions which gets called by our child component.
116
11:14.130 --> 11:22.500
For example, when we create each to do item in addition to the text property, we can add another property.
117
11:22.950 --> 11:24.990
So we could call it anything we want,
118
11:24.990 --> 11:35.240
so say onChecked maybe. And we can set this equal to a function inside our app component.
119
11:35.240 --> 11:42.740
So let's say that we wanted to create a function called deleteItem and this was the function that
120
11:42.740 --> 11:46.740
we would pass to our child ToDoItem.
121
11:46.760 --> 11:56.270
So we'll call it deleteItem and then inside our ToDoItem we'll now be able to trigger, instead of handle
122
11:56.270 --> 11:59.300
Click which lives inside the ToDoItem,
123
11:59.300 --> 12:08.410
we can now trigger props.onChecked. And this means that it'll be able to trigger deleteItem inside
124
12:08.470 --> 12:10.670
all app component right here.
125
12:10.720 --> 12:13.080
So let's go ahead and just log this event,
126
12:15.820 --> 12:16.390
like so.
127
12:17.050 --> 12:24.370
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
12:24.370 --> 12:26.570
this message being sent.
129
12:26.590 --> 12:35.360
So this entire function is being packaged up and sent over to the to-do item under the prop onChecked.
130
12:35.440 --> 12:40.790
And then that prop is only triggered when this div detects a click.
131
12:40.960 --> 12:50.240
And now I can address what should happen when that item wants to delete itself from the array. If I wanted
132
12:50.240 --> 12:57.260
to delete an item from my items array, I'm probably going to need to call setItems.
133
12:57.260 --> 13:05.300
And inside this set items, I'll need to get hold of the previous items and open up a set of curly braces
134
13:05.360 --> 13:07.810
for my arrow function.
135
13:08.090 --> 13:14.210
Now how can i delete the item that requested the deletion?
136
13:14.430 --> 13:22.920
Well one of the things I could do is when I call this onChecked, I could pass over something that identifies
137
13:23.010 --> 13:30.180
this particular ToDoItem from all of the other ones that gets rendered. And I can do that by adding
138
13:30.300 --> 13:31.860
an id.
139
13:31.950 --> 13:34.800
So inside my to do list item,
140
13:34.850 --> 13:41.400
firstly we need to remember that when we map through arrays and create components we should always have
141
13:41.460 --> 13:42.900
a key.
142
13:42.900 --> 13:51.840
And I'm going to set this key to the index of the ToDoItem from the items array. And this map function
143
13:51.960 --> 13:55.520
actually gives me a really easy way of accessing that.
144
13:55.530 --> 14:01.240
Notice how in this callback function the first item I provide is the value,
145
14:01.260 --> 14:05.650
so the value of each item from the array.
146
14:05.670 --> 14:08.900
So in this case its the do item that I've already tapped into.
147
14:09.420 --> 14:18.360
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
14:18.360 --> 14:19.370
items array.
149
14:19.440 --> 14:20.940
So let's try doing that.
150
14:21.210 --> 14:28.440
Remember from our lesson on arrays that if you only have one parameter, you can leave out the parentheses.
151
14:28.560 --> 14:34.140
But if you want to have more than one parameter then you'll need the parentheses and add a comma to
152
14:34.140 --> 14:35.880
separate each of those items.
153
14:36.510 --> 14:39.120
So the second one I'm going to call index.
154
14:39.120 --> 14:44.630
This is going to be the index of the current ToDoItem that's being looped through the items array
155
14:45.210 --> 14:49.220
and I'm going to use the index as the key. Now
156
14:49.250 --> 14:56.600
in an ideal world, React advises us to not use the index for the key and instead use some sort of unique
157
14:56.690 --> 14:58.820
identifying string.
158
14:58.880 --> 15:05.870
For example you could use a package such as uuid which basically just allows you to generate a u
159
15:05.890 --> 15:09.160
uid by installing it as a package.
160
15:09.170 --> 15:16.070
So super simple. But in our case we're going to leave it as the index because we want to use this index
161
15:16.070 --> 15:21.890
value and we want to use it to pass over to our ToDoItem as an id.
162
15:25.040 --> 15:30.830
So now that means our ToDoItem should be able to receive the id in the props.
163
15:30.830 --> 15:33.160
Let's see if that is indeed the case.
164
15:33.380 --> 15:39.470
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
15:39.470 --> 15:44.570
id being passed over as 0 because it's the first item in the array.
166
15:44.570 --> 15:48.210
This one has a id of one because it's the second item.
167
15:48.410 --> 15:54.560
They both get some text as the props and they also get this method onChecked which corresponds to the
168
15:54.560 --> 15:56.730
deleteItem function.
169
15:56.960 --> 16:04.220
So you might think that given that we have access to the id now inside this function, I could just
170
16:04.220 --> 16:13.640
pass it back over as props.id and that way my onChecked function will be able to receive the id
171
16:13.760 --> 16:18.440
right here and then I could probably just log it.
172
16:18.530 --> 16:22.160
Now I'm going to show you what actually happens instead.
173
16:22.160 --> 16:29.480
If I go ahead and keep the console open and I add a new item, you'll see that immediately this console
174
16:29.480 --> 16:33.480
log triggers and it prints out the id of the item.
175
16:33.620 --> 16:35.370
So what's going on here?
176
16:35.480 --> 16:41.350
Now remember that way back and we talked about JavaScript functions and passing around functions.
177
16:41.420 --> 16:47.360
We said that whenever you have a set of parentheses and something inside it, then you're going to be
178
16:47.360 --> 16:51.570
calling the function rather than passing the function.
179
16:51.770 --> 16:56.570
So we can't actually pass our props.id like this.
180
16:56.570 --> 17:01.920
This is just going to call it immediately the moment that this ToDoItem gets rendered.
181
17:02.030 --> 17:11.000
Instead what we can do in our onClick is to create a function and this function will only get called
182
17:11.360 --> 17:17.660
when the onClick gets triggered and it's only in this moment that we actually want to tap into props
183
17:18.080 --> 17:28.530
.onChecked and then passing in the props.id. This way when the ToDoItem gets rendered,
184
17:28.540 --> 17:34.990
this function is not going to be called until this div detects a click.
185
17:34.990 --> 17:41.800
Let me reiterate this. Previously when we click the Add button and rendered the ToDoItem component
186
17:42.100 --> 17:49.570
it triggered props.onChecked immediately. But now props.onChecked will only be called the moment
187
17:49.600 --> 17:52.400
the item is actually clicked.
188
17:52.400 --> 18:03.350
Now if I add an item click Add, notice that nothing gets printed until I click on this ToDoItem. Now
189
18:03.350 --> 18:10.090
that we know we can get hold of the id of the ToDoItem inside this delete function.
190
18:10.190 --> 18:18.590
Well now we can activate our setItems and we can use what we've learned before to change our items
191
18:18.770 --> 18:19.850
array.
192
18:20.030 --> 18:26.810
More specifically we're going to use the filter function that we learned before and filter through all
193
18:26.810 --> 18:37.070
of the previous items in our items array and get rid of the ones which match this id. So inside our
194
18:37.070 --> 18:38.280
setItems
195
18:38.510 --> 18:47.110
we're going to return a array and that array is going to be constructed from our previous items array.
196
18:47.300 --> 18:53.320
But it's going to be filtered so that we get rid of the item that we don't want,
197
18:53.450 --> 19:00.680
so the one that has this id that was passed over. Remember that the filter function again expects
198
19:00.890 --> 19:02.730
a function itself.
199
19:02.750 --> 19:11.000
Let's go ahead and add a arrow function and inside this filter function, we have access to two properties.
200
19:11.150 --> 19:16.350
The first one is the actual element that's being looped through in the array
201
19:17.000 --> 19:20.730
and the second one is the index of the current element.
202
19:21.020 --> 19:28.520
So we can tap in to each item as we're looping through previous items and we can also tap into the index
203
19:28.730 --> 19:31.110
of that item.
204
19:31.160 --> 19:38.720
Now we're going to go through our array of previous items, look through each item and get the index of
205
19:38.720 --> 19:48.260
each item and we're going to return an output, a final array, that is going to return all the items where
206
19:48.260 --> 19:53.510
the index is not equal to the id being passed over.
207
19:54.110 --> 19:58.670
So let's go ahead and hit save and check this out inside our new window.
208
19:58.670 --> 20:03.800
So I've got my React dev tool open here as well and I'm going to add a new item.
209
20:04.370 --> 20:12.050
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
20:12.050 --> 20:15.290
the ToDoItem component as a prop.
211
20:15.290 --> 20:21.860
And in addition, there's another prop, onChecked, which passed over the deleteItem function that we were
212
20:21.860 --> 20:30.080
editing here. And I've set up the code such that when I click on this ToDoItem, it will trigger delete
213
20:30.110 --> 20:38.630
Item and it will pass over the id as the input. Then we get to receive that id here which remember
214
20:38.690 --> 20:44.070
equates to the index of the item that we're trying to delete.
215
20:44.270 --> 20:53.360
And then we call setItems to update our list of items and we filter it so that we only end up with
216
20:53.360 --> 20:56.900
the ones where the index doesn't match this id,
217
20:56.900 --> 21:00.770
so everything other than the one that's being clicked.
218
21:00.770 --> 21:04.180
So let's add a couple more.
219
21:04.640 --> 21:12.700
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
21:12.710 --> 21:13.910
deleteItem.
221
21:14.060 --> 21:21.950
It's going to loop through my array of three items and filter so that we only end up with items where
222
21:21.950 --> 21:25.090
the index does not equal one.
223
21:25.100 --> 21:27.550
So that's this one and this one.
224
21:27.560 --> 21:31.300
So that means if I click here, you can see eggs disappears
225
21:31.430 --> 21:40.060
but we end up with still the remainder of our array that's been filtered using this rule.
226
21:40.160 --> 21:45.050
This really is getting into a lot of functional programming that you'll see a lot of
227
21:45.050 --> 21:54.020
in React where we're passing one function into another function inside another function inside another
228
21:54.020 --> 21:57.110
function inside another function.
229
21:57.650 --> 22:04.280
It does take some time to be able to wrap your head around this and I strongly encourage you to have
230
22:04.280 --> 22:11.210
a go at playing around with this code, trying to filter on some sort of different condition maybe changing
231
22:11.210 --> 22:15.980
the items so that instead of being an array, it's an array of objects.
232
22:15.980 --> 22:20.110
Just have a mess around with it until you get comfortable with it.
233
22:20.510 --> 22:26.270
And it always takes a lot of time to mess around with these things especially something as complex as
234
22:26.270 --> 22:33.200
this to feel comfortable. And the idea is that I'm not expecting you to remember how to do this. Just
235
22:33.260 --> 22:36.950
so that you understand what's going on, so you can look it up the next time.