1 00:00:02,390 --> 00:00:07,550 Now with this action being dispatched, we can go to the reducer and write some code here in input change 2 00:00:08,170 --> 00:00:09,440 and that's very simple code. 3 00:00:09,440 --> 00:00:18,650 We return a new state where I copy my old state and I want to update the value with action.value 4 00:00:19,400 --> 00:00:25,650 because value is one property I have on this action, the other is isValid and that's the other thing which 5 00:00:25,650 --> 00:00:30,710 I also want to update on every keystroke, isValid for this input, not for the entire form but for this 6 00:00:30,710 --> 00:00:38,420 input managed internally. I'll not update touched here, instead touched should be updated when we actually 7 00:00:38,930 --> 00:00:43,490 lose focus because that means the user is done entering content for the moment, 8 00:00:43,490 --> 00:00:52,550 so now I want to mark this as touched and possibly show validation errors. So here, lost focus handler 9 00:00:52,580 --> 00:00:56,690 is a function which I add and there, I will dispatch 10 00:00:59,970 --> 00:01:01,890 an action. Now for that, 11 00:01:01,890 --> 00:01:04,470 I'll create an action identifier up here, 12 00:01:04,710 --> 00:01:16,170 input blur, like this, input blur like this and now we can dispatch input blur here, don't need to attach 13 00:01:16,170 --> 00:01:24,060 any data and now in our reducer, we can use that with another case, input blur and if that is the 14 00:01:24,060 --> 00:01:29,280 case, I return a new state where I copy the old state because I want to keep value and is valid and not 15 00:01:29,280 --> 00:01:36,620 change that but I want to set touched to true, so as soon as we blur an input, it is treated as touched. 16 00:01:36,750 --> 00:01:44,110 Now we just need to hook up this function, so I will set the onBlur listener here and point at lost focus 17 00:01:44,110 --> 00:01:46,870 handler so that this triggers when this input blurs. 18 00:01:49,850 --> 00:01:57,100 Now that is all nice and this will manage the state inside of this input, of course I still need to forward 19 00:01:57,190 --> 00:02:03,370 the value and the information, whether that input is valid or not, to my parent though, so to the edit product 20 00:02:03,370 --> 00:02:14,700 screen. Hence what we can do is we can add use effect here as an import in the input component and maybe 21 00:02:14,700 --> 00:02:15,310 add it here 22 00:02:18,430 --> 00:02:28,070 and in the use effect function, I want to call a function which I expect to get via props, maybe on input 23 00:02:28,130 --> 00:02:40,980 change, name is up to you and I will just execute this and forward my input state value and my input 24 00:02:40,980 --> 00:02:48,540 state is valid information, so that in the parent component, we can add a function here that listens to 25 00:02:48,540 --> 00:02:53,790 that in the end, that is triggered, which receives these two arguments and then in the parent, so in the screen 26 00:02:53,790 --> 00:03:00,310 component, we can update our state there. Now the important thing is when should this use effect run and 27 00:03:00,310 --> 00:03:07,420 it shouldn't run after every keystroke? Instead this should run whenever our input state value or is 28 00:03:07,420 --> 00:03:17,780 valid changes of course, because that's what we depend on or to be precise, we might even only send this 29 00:03:17,780 --> 00:03:25,910 information if input state touched is true because otherwise the parent component might not care. 30 00:03:26,100 --> 00:03:31,680 So now our dependency here is the entire input state because we use all values but we still will only 31 00:03:31,680 --> 00:03:36,600 fire this function on the parent component if touched is true, so this effect will run more often but 32 00:03:36,600 --> 00:03:42,240 most of the time it won't do anything as long as touched is not true and props of course is also our dependency, 33 00:03:42,480 --> 00:03:49,170 now to avoid an infinite rendering loop here, we can use the object destructuring syntax and pull out 34 00:03:49,650 --> 00:03:56,640 on input change, so that if other props change, we don't refire this effect because on input change is what 35 00:03:56,640 --> 00:04:01,890 we need down there and now we can add this is a dependency and the other things, other changes in the 36 00:04:01,890 --> 00:04:03,820 props will not trigger this. 37 00:04:04,260 --> 00:04:10,620 So now we call this on the parent component whenever this is touched and then we forward the value 38 00:04:10,650 --> 00:04:13,470 and the validity of this input to the parent component. 39 00:04:16,680 --> 00:04:23,400 So let's now make use of that in the edit product screen, there on our inputs like on the title input, 40 00:04:23,400 --> 00:04:29,670 we can add the on input change prop and that's the prop which I target here right, so you need to get 41 00:04:29,670 --> 00:04:34,230 that naming right and this expects to get a function as a value which then in turn will receive two 42 00:04:34,230 --> 00:04:38,140 arguments when called - the value and the validity of that input. 43 00:04:38,220 --> 00:04:44,490 So here I want to point at a function and that can be our let's say input change handler, we can recycle 44 00:04:44,490 --> 00:04:46,910 this, I just rename it to be more fitting. 45 00:04:47,130 --> 00:04:55,070 So the input change handler can be triggered here, like this, there I want to still pass in my identifier 46 00:04:55,100 --> 00:04:59,930 because I want to reuse that for all my inputs and now since I'm in the screen component, we will have 47 00:04:59,930 --> 00:05:07,940 multiple inputs and there, we therefore get our identifier and then we get the value and the is valid 48 00:05:07,940 --> 00:05:09,410 information, right? 49 00:05:09,410 --> 00:05:15,740 So input value and input validity, that's what we get, 50 00:05:15,740 --> 00:05:15,940 right, 51 00:05:15,950 --> 00:05:19,940 because that's what we forward from inside the input component here, 52 00:05:19,940 --> 00:05:22,510 that's what we pass to this on input change 53 00:05:22,580 --> 00:05:31,110 function. So therefore here, we don't need to do validation, we just need to forward that information. We 54 00:05:31,110 --> 00:05:38,310 forward the input value, we forward the input validity and this will only happen when the input is touched 55 00:05:38,310 --> 00:05:38,760 by the way, 56 00:05:38,790 --> 00:05:40,320 so when it loses focus 57 00:05:43,310 --> 00:05:48,010 and of course that's just a restriction I added here, that you need to have lost focus, you could 58 00:05:48,020 --> 00:05:53,060 of course also go to the input component and remove this if check to update the validity and value 59 00:05:53,060 --> 00:05:59,260 in the parent component for every input, for every keystroke. Now one additional thing which I want to 60 00:05:59,260 --> 00:06:07,390 do, I want to wrap this in a use callback call here, so that this function isn't rebuilt unnecessarily 61 00:06:07,510 --> 00:06:13,050 because keep in mind that we passed this function to on input change in the input component and there 62 00:06:13,060 --> 00:06:13,630 on the other hand, 63 00:06:13,630 --> 00:06:15,400 this is a dependency of this effect, 64 00:06:15,430 --> 00:06:18,190 so this effect will rerun whenever this changes. 65 00:06:18,190 --> 00:06:22,780 So it shouldn't change too often to avoid unnecessary effect runs 66 00:06:22,810 --> 00:06:28,900 and here we therefore need to state our dependencies and the only dependency we have is dispatch 67 00:06:29,080 --> 00:06:31,690 form state which will actually never change, 68 00:06:31,690 --> 00:06:32,430 so we're good, 69 00:06:32,430 --> 00:06:35,620 this should never rebuild because the logic in there doesn't change, 70 00:06:35,680 --> 00:06:43,690 we get all values that we need as arguments. With that in my edit product screen, 71 00:06:43,690 --> 00:06:50,310 I still have my forms that of course with all the form values and the form validities but I only manage 06:50.310 -- 06:55.600 this here because I need to submit it later and because I need to derive the overall form validity. I 72 00:06:55,600 --> 00:06:58,280 don't do it because I need to manage every input here, 73 00:06:58,390 --> 00:07:05,100 we do that from inside the input component instead. For submission and so on, we still need the information 74 00:07:05,100 --> 00:07:06,460 about every store, the value 75 00:07:06,500 --> 00:07:18,810 however. Now if I go back to the input, don't forget that you also need to set the initial value prop 76 00:07:19,320 --> 00:07:22,600 and whether this should be initially valid, 77 00:07:22,680 --> 00:07:31,290 so if I go back to the edit product screen, now we should set initial value here to what? 78 00:07:31,320 --> 00:07:35,640 Well, we can check if we have an edited product and if we do, then the initial value here should be 79 00:07:35,640 --> 00:07:37,150 editedProduct.title, 80 00:07:37,170 --> 00:07:44,760 otherwise it's an empty string and this should be initially valid, 81 00:07:45,480 --> 00:07:47,480 so use this prop here 82 00:07:49,830 --> 00:07:51,860 if we have an edited product, 83 00:07:51,870 --> 00:08:00,780 so if this is, with the double bang here, if this is true. If we have no edited product, then this can't 84 00:08:00,780 --> 00:08:01,530 be initially valid 85 00:08:01,530 --> 00:08:03,390 so then I pass false which is good, 86 00:08:03,390 --> 00:08:07,210 which is just what should be the case. 87 00:08:07,280 --> 00:08:13,040 Now we can copy these two lines, also to the imageUrl, there of course we forward the image 88 00:08:13,040 --> 00:08:20,400 URL value as a default value, other than that, it can stay like this. For the price, we'll never 89 00:08:20,400 --> 00:08:25,650 have an initial value and it will never be initially valid. For description on the other hand, we can 90 00:08:25,660 --> 00:08:28,950 again add this and here point at description. 91 00:08:32,270 --> 00:08:39,050 Now with that, there is one thing I need to fix in the input component which I also just thought about, of 92 00:08:39,050 --> 00:08:43,450 course here, the form state is not available anymore, 93 00:08:43,450 --> 00:08:47,950 instead we have our internal state here and I named this input state, 94 00:08:47,960 --> 00:08:49,870 that's what my reducer returns, 95 00:08:50,120 --> 00:08:51,700 so we should use that here of course. 96 00:08:51,700 --> 00:08:57,830 So here we have inputState.value as a value, it's as simple as that 97 00:08:57,920 --> 00:09:03,590 and here we have inputState.isValid as a check and that should be all. 98 00:09:03,590 --> 00:09:10,060 Now if we save this and have a look at this, let's see whether that works. If we go here, we have all the 99 00:09:10,060 --> 00:09:13,360 error messages initially which makes sense because they're all invalid initially, 100 00:09:13,790 --> 00:09:20,500 if I start typing here, I get an error, can't find variable isValid in my input component, that has to be 101 00:09:20,500 --> 00:09:26,910 in that handler that fires for every keystroke, probably here, 102 00:09:26,920 --> 00:09:29,490 yes in the reducer of course I should check 103 00:09:29,490 --> 00:09:30,000 action.isValid, 104 00:09:30,000 --> 00:09:32,490 not just isValid, 105 00:09:32,730 --> 00:09:37,470 I have to get this from the action. So with that fix here, 106 00:09:37,480 --> 00:09:42,240 let's go back and let's try this again. If I start typing, this is cleared, if I 107 00:09:42,270 --> 00:09:46,530 revert this, my error doesn't come back 108 00:09:46,530 --> 00:09:50,800 though, so that that's a little error we'll have to fix. For the price, 109 00:09:50,800 --> 00:09:55,330 0 is then not treated as valid, one also 110 00:09:55,350 --> 00:09:58,590 isn't, so validation has some issues 111 00:09:58,590 --> 00:10:03,490 but entering and so on works but let's see what's wrong with validation now. 112 00:10:03,500 --> 00:10:07,090 Well of course the way validation works here, we're not using it. 113 00:10:07,100 --> 00:10:12,290 Please keep in mind that I always check whether this type of validation is enabled for this input or 114 00:10:12,290 --> 00:10:17,000 this type or this type and we do this with the help of these special props which we need to set. Right 115 00:10:17,030 --> 00:10:22,980 now no validation is enabled, so only the default invalid values are taken into account. 116 00:10:23,060 --> 00:10:28,610 So if I go to the edit product screen, we now enable this special validation by setting the right props, 117 00:10:28,610 --> 00:10:39,170 for example here on the title input, required. On the imageUrl, also required. On the price here, required 118 00:10:39,470 --> 00:10:47,540 and also maybe min equal to zero, so that zero is the minimum number we have to enter or a number greater 119 00:10:47,540 --> 00:10:48,350 than that actually, 120 00:10:48,380 --> 00:10:58,480 so maybe 0.1 is our minimum number. For the description, should also be required and maybe also min length 121 00:10:58,540 --> 00:11:05,100 equal to 5, to have at least 5 characters. There also is one additional thing we need to do here in the 122 00:11:05,100 --> 00:11:11,070 edit product screen, this on input change handler here also needs to be added to every input of course, 123 00:11:11,130 --> 00:11:16,870 not just to the title input. So we need to add this for the imageUrl, otherwise we'll get an error 124 00:11:16,870 --> 00:11:26,760 later, we'll need to add this here to the price input, we'll also need to add this here obviously to 125 00:11:26,760 --> 00:11:30,580 the description input and replace the identifier in the right way. 126 00:11:32,810 --> 00:11:36,010 So let's test this and you'll notice something strange, 127 00:11:36,040 --> 00:11:39,000 if you type here, that works, if you 128 00:11:39,000 --> 00:11:41,210 type in the imageUrl, doesn't work right, 129 00:11:41,220 --> 00:11:47,300 the validation doesn't update and you can't go back either, at least this might be stuck. 130 00:11:47,310 --> 00:11:54,990 So if we reload this with command error or double pressing R on Android and we give this another try here, 131 00:11:55,010 --> 00:12:00,950 let's say on the price you'll notice this works but why doesn't it work and the same would be the 132 00:12:00,950 --> 00:12:05,630 case by the way if you tried it on the imageUrl, here 133 00:12:05,670 --> 00:12:10,200 this works. Why does it work if I previously edited another input because now for example, it doesn't work 134 00:12:10,200 --> 00:12:15,100 on a title anymore? And the problem is related to this being stuck here. 135 00:12:15,390 --> 00:12:22,500 We're rerendering this component way too often and the reason for that is really hard to trace down. 136 00:12:23,010 --> 00:12:24,980 In the end, we have kind of an infinite loop here and 137 00:12:24,990 --> 00:12:27,290 the problem is our input change handler. 138 00:12:27,420 --> 00:12:34,290 We're using callback here to avoid unnecessary recreation but by then binding it like this, we kind 139 00:12:34,290 --> 00:12:40,710 of render this obsolete, it's making no difference now because this will create a new function binding 140 00:12:40,800 --> 00:12:42,230 for every rerender cycle. 141 00:12:42,750 --> 00:12:49,590 So in the end what we have to do is we have to remove bind here, remove it here in all these places because 142 00:12:49,590 --> 00:12:51,550 this will break our app, it will cause 143 00:12:51,780 --> 00:12:56,980 too many rerender cycles because of use callback not having an effect 144 00:12:57,210 --> 00:13:01,200 and instead we might want to forward an ID 145 00:13:01,200 --> 00:13:05,850 prop of title and so on to our input component. 146 00:13:06,050 --> 00:13:14,200 So let's add this on all these inputs, this ID prop is basically what I added in the bind call before, 147 00:13:14,260 --> 00:13:18,860 so we just forward this to the input component so that we can use it in there, 148 00:13:18,880 --> 00:13:23,430 let's do this for all these inputs here and now how can we use that? 149 00:13:23,470 --> 00:13:27,910 Well inside of the input, we now have this ID prop available right, 150 00:13:27,910 --> 00:13:30,820 so there we can simply attach this. 151 00:13:30,880 --> 00:13:38,280 So here we can forward props.id or simply ID so that we don't have to add props here and 152 00:13:38,280 --> 00:13:44,370 instead pull out ID out of props here just as we did it with on input change and add ID 153 00:13:44,470 --> 00:13:50,470 as a dependency of this effect thereafter. And now with that, we still forward the identifier of this input 154 00:13:50,830 --> 00:13:55,630 but in a more elegant way that avoids using bind which in the end recreates this function for every 155 00:13:55,630 --> 00:13:59,000 render cycle and hence leads to an infinite loop 156 00:13:59,140 --> 00:14:03,430 and therefore now if we reload this and we go to the admin section here, 157 00:14:06,890 --> 00:14:15,490 now I can enter something here and also here and now this works because now we avoid this infinite 158 00:14:15,490 --> 00:14:19,660 render cycle and we get the validation as we wanted. 159 00:14:19,690 --> 00:14:26,170 So now this is my approach or this is the approach I wanted to show you here, which you can use to have 160 00:14:26,170 --> 00:14:31,630 a reusable input component which does its own validation, which you can configure from outside and where 161 00:14:31,630 --> 00:14:33,960 you can still manage the overall form. 162 00:14:34,060 --> 00:14:36,120 With that, let's dive into some finetuning, 163 00:14:36,220 --> 00:14:42,070 like for example styling this error message we're displaying, this validation error message and also 164 00:14:42,070 --> 00:14:45,310 controlling when we display it because I don't want to show it all the time.