WEBVTT 0 00:00.120 --> 00:07.140 Hey guys. In this lesson I want to talk about ways of managing more complex state. And what do I mean 1 00:07.140 --> 00:15.960 by complex? I mean managing the state of Javascript objects where you might have to retrieve the previous 2 00:15.960 --> 00:21.400 value of the object. For example of added an input to the website 3 00:21.540 --> 00:27.600 and I want to be able to greet the user using both of these pieces of information so that when they 4 00:27.600 --> 00:33.040 type their first name, it says hello and then adds the first name to the

5 00:33.270 --> 00:39.190 and then when they add their last name, the first name is still there and it's still stateful. 6 00:39.350 --> 00:44.050 And the last name also updates and is independent from the first name. 7 00:44.070 --> 00:46.080 Let's think about how we might do this. 8 00:46.080 --> 00:53.670 And I want you to go ahead and fork the starting sandbox. And notice how we've got a very simple form 9 00:53.700 --> 00:57.300 with two inputs and a submit button. 10 00:57.300 --> 01:03.030 I want you to have a think about how you might, using the knowledge that you currently have, achieve this 11 01:03.030 --> 01:03.920 result. 12 01:04.050 --> 01:09.000 Pause the video, give it a go and then come back and we'll go through it together. And then I'll show you 13 01:09.000 --> 01:12.770 some other ways of doing it too. 14 01:13.230 --> 01:13.460 All right. 15 01:13.460 --> 01:18.220 So I hope you took this opportunity as a little bit of extra practice because this is the only way that 16 01:18.220 --> 01:23.010 you gonna get familiar with it and start turning it into your own muscle memory. 17 01:23.120 --> 01:29.180 The first thing I need is some sort of variable that can hold the value that's being typed into both 18 01:29.180 --> 01:30.190 of these inputs. 19 01:30.740 --> 01:37.100 So I'm going to create a stateful constant which I'll call fName for first name. And then I'm going 20 01:37.100 --> 01:41.570 to name a method that I'm gonna use to set this first name constant 21 01:41.870 --> 01:50.540 so call it setFName. And then of course I have to call the useState method in order to make this constant 22 01:50.660 --> 01:53.120 change and update and be stateful. 23 01:53.180 --> 01:55.660 And remember this of course comes from the React modules 24 01:55.670 --> 01:57.080 so we have to import it too. 25 01:57.920 --> 02:03.170 And then when I call this useState method I get to provide an initial state. 26 02:03.230 --> 02:11.370 So I'm gonna set it to just be an empty string because I'm going to use that as the value of my input. 27 02:11.540 --> 02:16.170 And this is of course going to correspond to that constant fName. 28 02:16.370 --> 02:19.950 And I'm also going to add it to the end of my

, 29 02:19.970 --> 02:27.080 so fName is also gonna be used here. And then I'm just gonna repeat this process for my last name. 30 02:27.080 --> 02:34.820 So change this to lName, setLName and the initial states also are going to be an empty string. 31 02:35.060 --> 02:43.210 And then we're going to use that to control this inputs so that the value matches up with our state. 32 02:43.220 --> 02:47.860 And I'm also going to add it to the end of my

. 33 02:48.210 --> 02:55.030 So now all I have to do is to call setFName and setLName when they change. 34 02:55.080 --> 03:04.200 So let's go ahead and add the onChange prop to our inputs and we're going to make it call a function 35 03:04.860 --> 03:15.830 and we call this updateFName and get hold of the event and then call it here. 36 03:18.650 --> 03:22.330 And when this gets triggered we get past the event, 37 03:22.400 --> 03:29.570 so whatever happened to this input. And we can create a new constant which is going to be called first 38 03:29.570 --> 03:37.490 Name and we're going to set it to the event.target.value. And then we're going to update our 39 03:37.490 --> 03:47.850 fName constant using these setFName method and we're going to pass it this new value. 40 03:48.090 --> 03:53.600 So now when I go ahead and write my first name in here, you can see it update. 41 03:53.820 --> 04:03.550 And if I copy this and just update it for my last name, change it in all the places and also add the 42 04:03.550 --> 04:12.190 onChange to my input, now you can see that when I type my first name it adds the first name. 43 04:12.230 --> 04:17.880 When I type my last name it has the last name. And that's the solution to the challenge. 44 04:18.050 --> 04:24.200 But you might have noticed through this experience while it's great practice, it's a little bit painful 45 04:24.340 --> 04:24.990 right? 46 04:25.040 --> 04:29.790 We have so many functions and we have two separate constants. 47 04:29.990 --> 04:35.270 Even though if you think about it when you create a Contact app or when you create some sort of input 48 04:35.270 --> 04:40.910 form, these pieces of information probably should be associated with each other 49 04:40.940 --> 04:41.230 right? 50 04:41.240 --> 04:47.660 The first name and the last name probably should belong inside the same Javascript object. 51 04:47.660 --> 04:49.430 So how can we do that? 52 04:49.430 --> 04:53.990 How do we manage a more complex piece of state? 53 04:53.990 --> 05:02.420 Well we can simply use useState but instead of storing a simple value, we can actually get it to store 54 05:02.540 --> 05:04.610 an object as well. 55 05:04.670 --> 05:10.080 Let's go ahead and delete all of the duplicated bits of code for the last name. 56 05:10.430 --> 05:17.350 And then I'm going to change this constant to just be called fullName. And then we're going to change 57 05:17.350 --> 05:24.650 this to setFullName and then we get to specify the initial state. The initial state is going to be 58 05:24.740 --> 05:25.750 a object 59 05:25.850 --> 05:32.270 and the first key is going to be fName with the value being nothing, an empty string. 60 05:32.270 --> 05:35.540 The second key is going to be the last name with 61 05:35.540 --> 05:38.460 again nothing as the value. 62 05:38.480 --> 05:43.460 So now our full name is storing a object. 63 05:43.640 --> 05:50.070 And when we call setFullName ideally, we want to set this to a new object. 64 05:50.240 --> 05:56.120 So let's get rid of this function and also update the code in our return statement. 65 05:56.120 --> 06:03.890 So instead of fName, this is going to become a fullName.fName and the same is going to happen 66 06:03.950 --> 06:09.290 with our last name because we're fetching it out of this object now. And then we're going to do the same 67 06:09.350 --> 06:12.220 for values here as well. 68 06:12.440 --> 06:18.080 And then on the onChange instead of calling two separate methods, I'm going to get it call the same 69 06:18.080 --> 06:27.850 method which we'll call handleChange. And I'm going to call that both here and here. 70 06:27.850 --> 06:34.180 So the idea is that when either of these inputs are changed, they're going to call the same function 71 06:34.510 --> 06:42.670 passing over the event that calls this change. And then inside this function, we're going to get hold 72 06:42.760 --> 06:50.470 of the new value which we'll just call newValue, and I'm gonna set it to event.target.value. 73 06:51.790 --> 06:57.850 But then I want to somehow be able to get hold of the previous value of the full name, 74 06:57.880 --> 07:04.360 so this object essentially, so that I can add to it the parts that have been changed. 75 07:04.420 --> 07:12.340 So if this input changes, then I only want to update the value of the fName and if this input changes 76 07:12.400 --> 07:14.450 I only want to update the lName. 77 07:14.620 --> 07:23.530 The other part should stay as it was so. Sow could we know which input actually triggered the handle 78 07:23.530 --> 07:25.000 Change? 79 07:25.000 --> 07:31.840 Well notice how inside our inputs, we've got the property or attribute called name 80 07:32.320 --> 07:39.040 and this sets it to a particular value that we can check for when we get hold of the events in our handle 81 07:39.040 --> 07:40.030 change. 82 07:40.030 --> 07:47.970 So I could create a constant called inputName and set it to event.target.name. 83 07:48.790 --> 07:58.110 So now if we go ahead and log these things, the new value and also log our input name 84 07:58.540 --> 08:07.480 and now if I start typing in my first name, you can see that the input name is logged as fName which 85 08:07.480 --> 08:13.990 corresponds to the name of the input that triggered the event and the new value is equal to whatever 86 08:13.990 --> 08:16.360 it is that I typed inside. 87 08:16.360 --> 08:18.970 But if I type something in the last name 88 08:19.060 --> 08:21.910 notice how the input name changes 89 08:21.910 --> 08:23.980 and it also logs what I typed. 90 08:24.340 --> 08:29.500 But notice how these inputs are not actually showing what I'm typing 91 08:29.500 --> 08:35.370 even though you can hear that I'm definitely typing in earnest. What's going on here? 92 08:35.690 --> 08:41.350 Well remember that these are controlled components. So their value, 93 08:41.360 --> 08:47.290 so what they're showing inside here, is set to the fullName.fName. 94 08:47.480 --> 08:51.470 And we haven't actually got a way of setting that full name yet. 95 08:51.500 --> 08:56.690 So it's always showing the initial value which is just the empty string. 96 08:56.720 --> 09:04.010 So while we're testing, I'm just gonna go ahead and comment out these two lines so that we turn this into 97 09:04.100 --> 09:06.500 an uncontrolled component. 98 09:06.620 --> 09:12.620 And if you're really curious, you can actually Google for what the difference is with React controlled 99 09:12.740 --> 09:15.050 and uncontrolled components. 100 09:15.050 --> 09:18.080 In this case in the end we definitely want a controlled component. 101 09:18.080 --> 09:25.460 We want the value and the state to all be equal to the same thing. But just for now while we're testing 102 09:25.460 --> 09:33.590 notice how now I get to actually write stuff and the thing that I type in here, say Angela, is going to 103 09:33.590 --> 09:35.600 be the final new value 104 09:35.600 --> 09:43.190 once I'm done typing. And the fName corresponds the input where it came from. So I can use these two 105 09:43.190 --> 09:47.050 pieces of information to create my new object. 106 09:47.050 --> 10:00.450 right? And you might think that it's as simple as saying well, if the inputName is equal to fName well 107 10:00.450 --> 10:06.210 in this case then this new value is going to correspond to what the user typed in here. 108 10:06.930 --> 10:16.890 So in that case I'm going to call setFullName and inside here I'm going to pass over the key as f 109 10:16.890 --> 10:28.020 Name and the value as the new value. And then else if the the input name was equal to lName well then 110 10:28.020 --> 10:34.350 that means they must have typed something in here and in that case I'm going to set full name to equal 111 10:34.350 --> 10:38.190 to a new object with lName 112 10:38.190 --> 10:44.780 and then the value being the new value. And then we're just missing one equal sign there. 113 10:45.000 --> 10:52.470 So you might think that this would work but some of you might realize what will happen if I do this. 114 10:52.650 --> 10:57.860 Let's see what happens. If I start typing in the first name field, 115 10:57.930 --> 11:03.110 you can see that this fName is being rendered inside here in our

. 116 11:03.390 --> 11:07.290 But watch what happens when I start typing in my lName. 117 11:07.530 --> 11:13.470 You can see that it's deleted the previous value of the first name in my object. 118 11:13.980 --> 11:15.580 So what's actually going on here? 119 11:15.870 --> 11:23.220 Well this is another good opportunity to use the React dev tools. The dev tools inside code sandbox does 120 11:23.220 --> 11:28.590 work but it can take a little while to load, and sometimes it doesn't want to. 121 11:28.590 --> 11:34.260 So what I recommend to do instead is to pop it out in a new window and open up your Chrome developer 122 11:34.260 --> 11:37.470 tools and then go to the React section. 123 11:37.470 --> 11:45.330 So now if we select the app, you can see in addition to props we can also see our hooks. And if we expand 124 11:45.390 --> 11:52.380 the state of our object, you can see it starts out with fName equal empty string, lName equal empty 125 11:52.380 --> 11:53.180 string. 126 11:53.280 --> 12:01.860 But when I start typing here, you can see that it's deleted the lName key and value out of this object 127 12:01.950 --> 12:04.660 and it's only kept the fName. 128 12:04.800 --> 12:10.650 And then when I do the same with my last name, you can see it's deleted the first name property and it's 129 12:10.650 --> 12:12.330 only kept the last name. 130 12:13.680 --> 12:20.460 Essentially what we're doing is each time we're calling setFullName, we're replacing this entire object 131 12:20.760 --> 12:26.260 with an object that only has one property, either the fName or the lName. 132 12:26.430 --> 12:33.780 And this is not what we want. What we want to do instead is we want to get hold of the previous value 133 12:34.200 --> 12:42.570 of this full name object and then only add to it the parts which have been changed. In order to do this 134 12:42.690 --> 12:48.750 instead of just calling setFullName or whatever it is that you decided to name the function that's 135 12:48.750 --> 12:58.260 going to update or change this full name, instead of simply just adding the new value inside these parentheses, 136 12:58.260 --> 13:06.520 we can also pass it a function. And I'm going to use a arrow function in this case. 137 13:07.000 --> 13:14.020 And when setFullName is called, it can get access to the previous value. 138 13:14.020 --> 13:20.740 And if I simply just log this previous value in here you'll see what it's actually doing. 139 13:20.770 --> 13:27.720 So whenever I start editing my first name input, it's going to call handleChange. 140 13:27.730 --> 13:31.900 It's gonna pass the event that led to this change. 141 13:32.020 --> 13:35.650 I'm gonna have a new value and I'm gonna have an input name. 142 13:35.650 --> 13:43.870 Now when this happens I call setFullName and when setFullName gets called, I'm passing in a function. 143 13:44.530 --> 13:49.490 That function is gonna get hold of the previous value of full name. 144 13:49.630 --> 13:58.390 So as soon as I do this, you can see that it prints an object with fName empty, lName empty. 145 13:58.390 --> 14:08.230 And this is really the key. As we type into our inputs, React will re-render our app component. But our 146 14:08.230 --> 14:16.360 app has state right? In the form of the full name Javascript object. And React remembers the value of 147 14:16.360 --> 14:17.860 this object. 148 14:17.860 --> 14:24.340 This means that we can make use of this remembered value as we're updating our first name or our last 149 14:24.340 --> 14:25.360 name. 150 14:25.360 --> 14:31.690 So then the question really becomes, well how can we use a function that gives a different output depending 151 14:31.690 --> 14:34.070 on the previous value? 152 14:34.120 --> 14:43.690 Well we could say if the inputName, so the name of the input element, was equal to fName 153 14:43.690 --> 14:52.270 well in this case we're going to return a new object where the fName is corresponding to the new value 154 14:53.230 --> 15:00.580 and then the lName is corresponding to the previous value's lName. 155 15:00.580 --> 15:09.160 And this way we create a new object and only update the part that has been updated by the user. 156 15:09.160 --> 15:19.130 And we can complete this by adding an else if input name is equal to lName and make sure that we actually 157 15:19.130 --> 15:23.760 spell this exactly the same as the names in our inputs here. 158 15:24.710 --> 15:31.520 Well in this case that means the user must have edited this field and I'm going to return a different 159 15:31.550 --> 15:41.150 object. The fName in this case is going to be using the previous values fName. And the lName is going 160 15:41.150 --> 15:44.240 to be using the new value. 161 15:44.430 --> 15:53.880 So now if we go over to our app and I start typing my first name and we look at our app component, you 162 15:53.880 --> 15:59.270 can see this object has its first name property replaced with whatever I typed here. 163 15:59.850 --> 16:06.790 And if I edit my last name, it's now replacing the previous value of last name with my new value. 164 16:06.990 --> 16:14.110 But it's using the previous value for the first name. The last thing to remember to do is just do uncomment 165 16:14.140 --> 16:22.540 these lines so that the values of our inputs can correspond to the latest state so that we control this 166 16:22.540 --> 16:31.210 input using our state. Now I've tried to keep this code as expressive as possible so that you can actually 167 16:31.210 --> 16:32.620 see what's going on. 168 16:32.620 --> 16:39.970 But in reality we could probably write this code in a much simpler way. To start with instead of using 169 16:40.150 --> 16:48.070 our constant newValue equals event.target.value input name being event.target.name we could 170 16:48.070 --> 16:56.740 in fact use our object destructuring. So we could create a new constant open up a set of curly braces 171 16:56.860 --> 17:03.850 to create a new object literal where we tap into the value property and the name property and we set 172 17:03.850 --> 17:15.770 it to equal event.target. And this will replace both of these things and we now get to use name, name, 173 17:17.470 --> 17:20.790 value and value. 174 17:21.780 --> 17:26.070 And that makes it already a lot simpler looking right? Now 175 17:26.250 --> 17:28.700 just as a word of warning 176 17:28.770 --> 17:35.280 make sure that in the future when you're creating your own apps or in any of the exercises or challenges 177 17:35.970 --> 17:43.670 don't try to access the event or anything related to the event inside a stateful setter. 178 17:43.680 --> 17:50.760 So when we set fullName we're trying to update the state of this constant fullName. And inside here 179 17:50.760 --> 17:57.990 if you try to access event, let's say instead of getting the name we actually try to get event.target 180 17:58.260 --> 18:05.160 .name even though this seems like valid code, what's actually going to happen is you're going to get 181 18:05.310 --> 18:13.390 an error inside the console and it's going to warn you about synthetic events being reused. 182 18:13.440 --> 18:19.830 Now if you're curious I'd recommend heading over to the React docs and having a read about these synthetic 183 18:19.830 --> 18:27.960 events. But essentially what it boils down to is the fact that when these inputs are passing an event 184 18:28.080 --> 18:34.140 through these event listeners, the event that you actually get hold of is not a real event. It's a synthetic 185 18:34.140 --> 18:41.130 event that React has created. And you must never try to access those events when you're trying to use 186 18:41.160 --> 18:43.440 one of these stateful setters. 187 18:43.470 --> 18:48.300 So inside here, you should not ever use event. 188 18:48.300 --> 18:54.840 You should always have it outside somewhere over here for example and I recommend just getting used 189 18:54.840 --> 19:04.350 to using destructuring like this when you're trying to access the new value or the name of the inputs. 190 19:04.370 --> 19:10.160 Now we've covered quite a lot of things in this one lesson and there's a lot of complex topics being 191 19:10.160 --> 19:11.770 thrown around here. 192 19:11.930 --> 19:17.200 So if it's at all confusing, I really recommend to just delete everything 193 19:17.210 --> 19:23.420 go back to the starting version of the sandbox and try to see if you can get this behavior created by 194 19:23.420 --> 19:24.620 yourself. 195 19:24.620 --> 19:30.320 That way you might encounter some problems and you might have to debug some issues but at least you'll 196 19:30.320 --> 19:32.960 be able to make this knowledge your own. 197 19:33.340 --> 19:36.460 Now in the next lesson, I've got a challenge for you. 198 19:36.500 --> 19:41.660 So once you're ready and you're happy that you understand what's going on in this lesson, then head over 199 19:41.660 --> 19:43.510 there and complete the challenge.