1 00:00:02,200 --> 00:00:06,570 Before we continue with updating or editing the data or deleting it, 2 00:00:06,680 --> 00:00:10,810 let's also make sure however that we don't see the dummy data here initially but that we actually 3 00:00:10,810 --> 00:00:16,660 see a loading spinner and maybe also a message if there is no data to be loaded or an error message 4 00:00:16,840 --> 00:00:19,810 if we have an error and for that, 5 00:00:19,810 --> 00:00:23,400 let's start with the loading spinner. On the products overview screen, there 6 00:00:23,470 --> 00:00:28,270 I want to show a loading spinner whilst we're waiting for data to arrive. For that we first of all need 7 00:00:28,270 --> 00:00:32,430 to know whether we're loading or not and we can control that with use state, 8 00:00:32,440 --> 00:00:34,850 so with an internal state in this component. 9 00:00:35,140 --> 00:00:42,910 So there, we can add isLoading and set isLoading and then simply use state here like this and initially 10 00:00:42,910 --> 00:00:45,890 we're not loading, so I set the initial state to false 11 00:00:46,630 --> 00:00:51,610 and now here when we dispatch this, so here in use effect, I set isLoading to true 12 00:00:51,610 --> 00:00:56,130 of course but once we're done, I want to set it back to false. 13 00:00:56,170 --> 00:01:03,610 Now the good thing is dispatch here dispatches our fetch products action and therefore this here gives 14 00:01:03,610 --> 00:01:04,780 us a promise actually, 15 00:01:04,840 --> 00:01:10,690 so here I can add then and also catch later for error handling to do something once this is done. 16 00:01:10,870 --> 00:01:15,670 You can also use async await but it's not allowed to use it like this 17 00:01:15,670 --> 00:01:20,910 here in use effect, that's not simply how use effect should be used. 18 00:01:20,910 --> 00:01:25,910 So if you would want to use use async here, you would have to wrap this into a separate function which 19 00:01:25,910 --> 00:01:32,720 you have to create in your effect, like load products which is just a dummy wrapper function that looks 20 00:01:32,720 --> 00:01:37,350 like this, maybe with set isLoading in there as well. 21 00:01:37,350 --> 00:01:43,530 Now here you can add async and now then you can call load products like that right thereafter, simply 22 00:01:43,530 --> 00:01:46,530 a wrapper which is required due to the way use effect works, 23 00:01:46,530 --> 00:01:48,260 it must not return a promise, 24 00:01:48,300 --> 00:01:54,030 it would do so if you use async here and therefore, using async there is not allowed, this on the other 25 00:01:54,030 --> 00:01:55,940 hand is allowed. 26 00:01:55,950 --> 00:01:57,520 So now in here, we can use 27 00:01:57,540 --> 00:02:03,060 await here to wait for this dispatching to be done, which means this will automatically wait for 28 00:02:03,060 --> 00:02:08,670 the promise that's in there, so for the HTTP request and thereafter we set isLoading to false 29 00:02:08,670 --> 00:02:10,300 once this is done. 30 00:02:10,710 --> 00:02:16,380 Now to show a spinner, thankfully React Native got one for us, the activity indicator which will automatically 31 00:02:16,380 --> 00:02:18,810 look good for iOS and Android, 32 00:02:18,840 --> 00:02:21,950 so it will automatically get the default platform style 33 00:02:22,320 --> 00:02:27,630 and now we can render this if we're loading. So down there instead of returning the flat list, if isLoading 34 00:02:27,630 --> 00:02:34,740 is true, I will return a different piece of jsx, I'll return a new view here with my activity indicator 35 00:02:34,740 --> 00:02:42,040 in there. For that of course, you need to have view imported as well, so make sure that this is also available. 36 00:02:44,480 --> 00:02:49,370 Now with that we returned this view with the activity indicator, there you can set up the size to be 37 00:02:49,380 --> 00:02:58,310 large and the color to be Colors.primary for example and this of course requires you to have that 38 00:02:58,310 --> 00:03:05,830 colors constant be imported, so make sure that is available and last but not least, now I want to 39 00:03:05,830 --> 00:03:10,960 center this indicator, so here we can add a style which we of course could set up with a stylesheet 40 00:03:11,020 --> 00:03:18,880 or here quick and dirty, flex one justify content center, align items center. However actually I will 41 00:03:18,880 --> 00:03:23,010 use a stylesheet here because we'll need that in a different place later too. 42 00:03:23,110 --> 00:03:33,450 So here I will refer to styles let's say centered and import stylesheet from React Native and with 43 00:03:33,450 --> 00:03:41,640 that, then go down there, add my styles object after setting up the navigation options maybe, styles with 44 00:03:41,640 --> 00:03:50,890 Stylesheet.create, add the centered property in there which has this styling with flex justify 45 00:03:50,890 --> 00:03:57,450 content align items and now with that, we should see a centered activity indicator whilst we're loading. 46 00:03:57,570 --> 00:04:03,790 So if I save this and this reloads, Firebase is very fast so you don't see it that long but for a short 47 00:04:03,790 --> 00:04:09,580 amount of time, you see that loading spinner here, with the different look on iOS and Android of course. 48 00:04:12,900 --> 00:04:19,110 Now of course loading is one thing, sometimes you also just don't have any data that could be loaded. Let's 49 00:04:19,110 --> 00:04:25,740 say in a reducer, we're reaching out to product.json which is a source where no data can be 50 00:04:25,740 --> 00:04:28,750 fetched and therefore we end up with an empty screen. 51 00:04:29,150 --> 00:04:32,920 Well that's something we can do but it's not the best possible user experience, 52 00:04:33,060 --> 00:04:39,540 so I want to add another if check where I check if isLoading is false, so if we're not loading anymore and 53 00:04:41,530 --> 00:04:48,190 my products here, the length there is equal to zero which means we have no products. 54 00:04:48,190 --> 00:04:54,340 In that case, I will also return my centred content here but there I simply want to output a text where 55 00:04:54,340 --> 00:04:58,230 I say no products found, 56 00:04:58,240 --> 00:05:08,530 maybe start adding some and for that of course, you need to make sure you import the text component and 57 00:05:08,530 --> 00:05:15,880 you can also assign your font styles there or create a default text wrapper component which we used 58 00:05:15,880 --> 00:05:17,140 in earlier modules, 59 00:05:17,140 --> 00:05:22,900 for now I'll just have my text like this and this is indeed what we see there for now. Of course as soon 60 00:05:22,900 --> 00:05:31,360 as I switch this back to the correct URL where we actually do find data, it works. Now last but 61 00:05:31,360 --> 00:05:33,680 not least, you could also have an error. Let's say here, 62 00:05:33,700 --> 00:05:35,850 I don't have .json there, 63 00:05:35,860 --> 00:05:41,610 of course that is an error we would typically avoid because that's just a typo on our side but unfortunately 64 00:05:41,620 --> 00:05:48,010 Firebase servers are relatively robust. So to fake that they for example went down or something went 65 00:05:48,010 --> 00:05:48,520 wrong, 66 00:05:48,550 --> 00:05:54,850 I'll just break the URL in this way which is an invalid URL and now what we see is an infinite spinner. 67 00:05:55,120 --> 00:06:04,340 The reason for that is that we actually now don't have a valid response. So if we have a look at 68 00:06:04,340 --> 00:06:10,490 our action here and we console log this response data here, 69 00:06:14,420 --> 00:06:16,220 we see that as this reloads, 70 00:06:19,170 --> 00:06:20,950 fetching should happen immediately right 71 00:06:21,060 --> 00:06:23,130 but we don't get any output there, 72 00:06:23,160 --> 00:06:29,760 we don't see anything being logged from this, we just get a promise rejection warning at some point of time, 73 00:06:29,970 --> 00:06:32,470 so it looks like we're simply getting an error 74 00:06:32,820 --> 00:06:34,870 and right now, we're not handling an error. 75 00:06:34,890 --> 00:06:39,990 Now if you would be using promises without async await, you would simply add catch statement. 76 00:06:39,990 --> 00:06:41,720 Here we are using async await 77 00:06:41,720 --> 00:06:48,660 so what we do is we wrap this into a try block, all that code which we ultimately want to run if everything 78 00:06:48,660 --> 00:06:52,960 succeeds and catch any errors here, 79 00:06:52,960 --> 00:06:57,340 so a try catch block. That is where we get a potential error 80 00:06:57,340 --> 00:07:00,820 and now with that error gotten here, we can handle it, 81 00:07:00,820 --> 00:07:06,460 for example send it to our own analytic server or do whatever we want and then maybe also rethrow it, 82 00:07:06,670 --> 00:07:11,740 of course if all what you do is rethrowing it, you wouldn't have needed to add this try catch block but 83 00:07:11,740 --> 00:07:17,080 typically, you might want to do more here - send to custom analytics server or anything like that 84 00:07:17,080 --> 00:07:18,930 as I mentioned. 85 00:07:19,150 --> 00:07:23,800 So now we rethrow the error, still not better, still causes us to have an error 86 00:07:23,860 --> 00:07:29,890 and in addition to just having try catch around this, we should actually also add another check here 87 00:07:29,890 --> 00:07:32,890 before we unpack the response body, 88 00:07:32,890 --> 00:07:38,770 we should also check if response okay is wrong, if that's false. 89 00:07:38,770 --> 00:07:44,670 OK is a field you have available on this response object and this simply is true if the response is 90 00:07:44,680 --> 00:07:50,800 in the 200 status code range. If it's in a different range, for example because you're not authenticated 91 00:07:50,800 --> 00:07:55,360 or anything like that, then the fetch API by default would not throw an error. 92 00:07:55,360 --> 00:08:01,000 However I do want to throw an error in that case so that we always end up in the catch block if we have a 93 00:08:01,450 --> 00:08:07,780 400 or 500 status code or if we have a network request issue, for example if the request can't even leave 94 00:08:07,780 --> 00:08:08,790 the device. 95 00:08:08,980 --> 00:08:15,250 So here and that's fetch API specific to handle 400 and 500 status codes as well which normally would 96 00:08:15,250 --> 00:08:16,590 not cause an error, 97 00:08:16,600 --> 00:08:21,350 I'll also throw a new error here in that case where I say something went wrong, 98 00:08:21,370 --> 00:08:27,850 of course you could dive into the response body here in that case too and find out what is wrong there 99 00:08:28,150 --> 00:08:32,910 but I will just throw this generic error and now we'll definitely have an error 100 00:08:32,920 --> 00:08:38,750 if something goes wrong if we don't get back our data but still, we then just rethrow the error here. 101 00:08:38,770 --> 00:08:43,020 But the place where I ultimately want to handle it is my component here, 102 00:08:43,030 --> 00:08:50,640 my screen component because there, I then want to show an error message. So in there, we have our effect 103 00:08:50,830 --> 00:08:58,330 and just as we could use catch or the catch handler if you use then and catch or async await with try 104 00:08:58,330 --> 00:08:59,860 catch in the action, 105 00:08:59,920 --> 00:09:01,800 we can do it here as well. 106 00:09:01,840 --> 00:09:09,380 So here, I want to try dispatching this but I want to catch any potential errors we might get. 107 00:09:09,400 --> 00:09:10,830 So here I catch any errors 108 00:09:10,840 --> 00:09:16,810 this might yield and since I rethrow my error in there, that's what I just did here, right, 109 00:09:16,840 --> 00:09:19,370 ultimately the error will reach us here. 110 00:09:19,400 --> 00:09:22,750 So now we will ultimately end up in this catch block 111 00:09:23,080 --> 00:09:29,740 and now to show our error data, we can add another state - error and set error, you can name it however you 112 00:09:29,740 --> 00:09:30,250 want. 113 00:09:30,340 --> 00:09:38,690 Initially that's undefined because initially I have no error but then here, I will call set error and 114 00:09:38,690 --> 00:09:41,540 set this to error.message for example, 115 00:09:41,540 --> 00:09:43,670 so to the message of this error I'm getting. 116 00:09:46,500 --> 00:09:51,330 Still of course, I want to set loading to false thereafter, that doesn't change because we definitely 117 00:09:51,330 --> 00:09:52,330 are not loading anymore 118 00:09:52,350 --> 00:09:58,920 even if we got an error but now we also have this error state which we can use, we can use that to check 119 00:09:58,920 --> 00:10:01,490 it here. If we do have an error, 120 00:10:01,500 --> 00:10:08,820 so if that is true, I don't even need to continue, instead I want to return my centered jsx content 121 00:10:08,820 --> 00:10:10,130 here but there, 122 00:10:10,140 --> 00:10:18,620 I just want to output a text where I say an error occurred for example, like this. If we now do that, 123 00:10:18,650 --> 00:10:20,290 you'll see we see a spinner 124 00:10:20,320 --> 00:10:21,370 but then we see this error 125 00:10:21,380 --> 00:10:24,990 text. Now of course, this doesn't allow the user to do a lot, 126 00:10:24,990 --> 00:10:27,800 we can now just navigate away and then come back 127 00:10:29,820 --> 00:10:36,820 but we might want to give the user a way of trying again, maybe by adding a button here. So now here with 128 00:10:36,820 --> 00:10:45,860 button imported, we can go there and then here add this button component with a title of try again 129 00:10:45,870 --> 00:10:49,810 and of course it's totally up to you how you want to let the user handle this 130 00:10:49,920 --> 00:10:55,260 and there you could add an onPress handler and now to be able to load again, 131 00:10:55,350 --> 00:10:58,770 I will actually take this load products function here, 132 00:10:58,770 --> 00:11:04,290 move it out of the effect and make it a regular function of this component so that I can call it from 133 00:11:04,290 --> 00:11:06,030 inside use effect. 134 00:11:06,090 --> 00:11:10,740 Now however it also needs to be a dependency and therefore to avoid an infinite loop, 135 00:11:10,800 --> 00:11:19,350 it should be wrapped in use callback, so let's import use callback here and wrap this around this function 136 00:11:19,350 --> 00:11:20,920 here 137 00:11:20,980 --> 00:11:21,870 where the dependency 138 00:11:21,870 --> 00:11:23,760 then is my dispatch function, 139 00:11:23,800 --> 00:11:27,600 you can also add set isLoading and set error 140 00:11:27,640 --> 00:11:32,440 but all these functions will never change, therefore you could also omit them and therefore this will never 141 00:11:32,440 --> 00:11:34,480 be recreated which is good 142 00:11:34,630 --> 00:11:37,930 but now we can use load products as a dependency here in use effect 143 00:11:37,960 --> 00:11:43,120 and now since it's not inside of the use effect function anymore, we can use load products anywhere else 144 00:11:43,120 --> 00:11:45,750 in this component and the anywhere else part is here 145 00:11:45,750 --> 00:11:51,700 this button, if we press this, I want to also trigger load products again and also last but not least, 146 00:11:52,000 --> 00:12:02,260 give the button color of colors.primary. With that, it looks like I have an error here with my imports, 147 00:12:02,260 --> 00:12:07,590 yes I already had button imported, so let's get rid of the second import, only import it once 148 00:12:07,780 --> 00:12:09,480 and now what you'll get is that 149 00:12:09,520 --> 00:12:16,450 this will actually reload, eventually throw an error and you can press try again to reload this, 150 00:12:16,460 --> 00:12:20,810 however we always see the error message simply because we never clear the error 151 00:12:20,840 --> 00:12:22,850 and if we do have an error, we always return this, 152 00:12:22,850 --> 00:12:25,220 we don't even make it to the loading spinner. 153 00:12:25,220 --> 00:12:31,010 So we should make sure that whenever we do load our products here, we set the error back to null, so we 154 00:12:31,010 --> 00:12:36,780 reset the error. By the way, multiple set state calls next to each other will be batched together by React, 155 00:12:36,800 --> 00:12:39,550 so this will not lead to multiple component re-render cycles, 156 00:12:39,560 --> 00:12:41,730 it's okay to use it like this. 157 00:12:41,750 --> 00:12:46,490 So now with that if I click try again, we can try again. Obviously it will never succeed because 158 00:12:46,490 --> 00:12:53,540 our URL is broken but still, this is how we could handle this error. With that however, I'll go back 159 00:12:53,540 --> 00:12:57,610 to my actions and fix that error by correcting this URL again 160 00:12:57,740 --> 00:13:00,560 and now this should also load our data correctly and display it.