1 00:00:00,000 --> 00:00:03,423 [MUSIC PLAYING] 2 00:00:03,423 --> 00:00:15,980 3 00:00:15,980 --> 00:00:17,980 SPEAKER: Hello, and welcome back for lecture 10. 4 00:00:17,980 --> 00:00:20,980 This week, we'll be talking about asynchronous Redux and some tooling 5 00:00:20,980 --> 00:00:22,900 around JavaScript. 6 00:00:22,900 --> 00:00:25,630 So in the previous lecture we talked about scaling complexity 7 00:00:25,630 --> 00:00:27,880 and some of the issues that a company like Facebook 8 00:00:27,880 --> 00:00:30,042 runs into when they're running at scale. 9 00:00:30,042 --> 00:00:31,750 We talked about their particular solution 10 00:00:31,750 --> 00:00:35,350 to that problem, which is the Flux architecture, whereby 11 00:00:35,350 --> 00:00:38,020 data takes a one-way street, basically. 12 00:00:38,020 --> 00:00:42,130 There are no two-way bindings between models and the controllers. 13 00:00:42,130 --> 00:00:46,540 We then talked about Redux, which is an implementation of Flux. 14 00:00:46,540 --> 00:00:49,600 And we talked about a lot of the things that you 15 00:00:49,600 --> 00:00:52,970 do in order to implement Redux in your current project. 16 00:00:52,970 --> 00:00:57,400 We actually implemented our own version of Redux, which we called simpleRedux, 17 00:00:57,400 --> 00:01:01,600 and in that implementation we have things like reducers, the store, 18 00:01:01,600 --> 00:01:03,100 and some actions. 19 00:01:03,100 --> 00:01:07,210 And we wrapped up last week by looking at this library called react-redux, 20 00:01:07,210 --> 00:01:10,900 which is React bindings which allow us to use Redux along 21 00:01:10,900 --> 00:01:14,410 with a project written in React. 22 00:01:14,410 --> 00:01:17,440 So I thought we should start off this week by reviewing react-redux 23 00:01:17,440 --> 00:01:19,520 and what it gives us. 24 00:01:19,520 --> 00:01:23,200 So, again, react-redux are some React bindings for Redux 25 00:01:23,200 --> 00:01:24,740 that give us a couple of things. 26 00:01:24,740 --> 00:01:28,690 One is this concept of a provider, and another is this higher-order component 27 00:01:28,690 --> 00:01:30,520 called connect. 28 00:01:30,520 --> 00:01:32,770 So, again, what provider does is it gives our children 29 00:01:32,770 --> 00:01:34,090 access to a Redux store. 30 00:01:34,090 --> 00:01:36,070 That way, every single child doesn't have 31 00:01:36,070 --> 00:01:40,690 to do something like import our store from our store file. 32 00:01:40,690 --> 00:01:44,950 Instead, what they do is they use this higher-order component called connect, 33 00:01:44,950 --> 00:01:47,800 which helps us to subscribe to any subset of our store 34 00:01:47,800 --> 00:01:50,840 and bind our action creators to our dispatch function. 35 00:01:50,840 --> 00:01:53,390 And so what exactly does that look like in code? 36 00:01:53,390 --> 00:01:58,600 So, as you recall, last week we added Redux to our app. 37 00:01:58,600 --> 00:02:01,060 And one place that was listening to our Redux store 38 00:02:01,060 --> 00:02:04,660 was this page called the ContactListScreen. 39 00:02:04,660 --> 00:02:08,930 40 00:02:08,930 --> 00:02:13,720 So in this ContactListScreen, we have, at the very bottom here, 41 00:02:13,720 --> 00:02:18,250 export default connect mapStateToProps ContactListScreen. 42 00:02:18,250 --> 00:02:20,830 And so the connect function comes from react-redux, 43 00:02:20,830 --> 00:02:23,280 as we see imported up here. 44 00:02:23,280 --> 00:02:26,240 And so what exactly does this connect function do? 45 00:02:26,240 --> 00:02:29,592 Well, if you recall, it takes a couple of different arguments, 46 00:02:29,592 --> 00:02:32,050 the first of which is this function called mapStateToProps. 47 00:02:32,050 --> 00:02:35,100 And I've only called it mapStateToProps here by convention. 48 00:02:35,100 --> 00:02:36,350 I can call it anything I want. 49 00:02:36,350 --> 00:02:42,760 I could call this getState, or getPropsFromStates, or really 50 00:02:42,760 --> 00:02:45,620 whatever I want, as long as what I declared 51 00:02:45,620 --> 00:02:48,160 matched the name of the function that I passed in here. 52 00:02:48,160 --> 00:02:51,790 So in this case, it would be getPropsFromState. 53 00:02:51,790 --> 00:02:56,410 And so what this function does is it takes the state object 54 00:02:56,410 --> 00:02:58,330 which it receives from the store-- 55 00:02:58,330 --> 00:03:01,000 and, again, the way that you get the state from the store 56 00:03:01,000 --> 00:03:04,420 is that store.getState, which we don't have to worry about. 57 00:03:04,420 --> 00:03:07,090 React-redux handles all of that for us. 58 00:03:07,090 --> 00:03:10,840 But what this function does is it takes the state and extracts from the state 59 00:03:10,840 --> 00:03:14,630 exactly the props that we want to listen to in this particular component. 60 00:03:14,630 --> 00:03:18,730 And so, in this component, we determined that we wanted a prop called contacts 61 00:03:18,730 --> 00:03:19,870 to be passed down. 62 00:03:19,870 --> 00:03:22,840 And where did that contacts get its data from? 63 00:03:22,840 --> 00:03:27,670 Well, it's the state key, it's the contacts key in that state object. 64 00:03:27,670 --> 00:03:30,760 And so, by defining this function that takes in the state 65 00:03:30,760 --> 00:03:34,420 and returns an object where the key called contacts gets the value 66 00:03:34,420 --> 00:03:38,410 state.contacts, that's exactly how we determine the props that 67 00:03:38,410 --> 00:03:43,810 are then passed into this class. 68 00:03:43,810 --> 00:03:46,870 And so one thing that may be confusing to you 69 00:03:46,870 --> 00:03:51,070 is this connect function prototype. 70 00:03:51,070 --> 00:03:53,080 So, generally, what we've seen thus far is 71 00:03:53,080 --> 00:04:00,160 something like a function called connect that might take one, or two, or maybe 72 00:04:00,160 --> 00:04:01,490 even three arguments. 73 00:04:01,490 --> 00:04:06,085 So it would look like getPropsFromState, maybe map-- 74 00:04:06,085 --> 00:04:10,600 75 00:04:10,600 --> 00:04:12,400 I forget what the exact function is called, 76 00:04:12,400 --> 00:04:16,462 but something like map or bindDispatchToActions. 77 00:04:16,462 --> 00:04:19,810 78 00:04:19,810 --> 00:04:22,840 And then maybe something like the component that you want to receive, 79 00:04:22,840 --> 00:04:24,920 so something like ContactListScreen. 80 00:04:24,920 --> 00:04:32,610 81 00:04:32,610 --> 00:04:35,850 Like this, where you have a function which takes in some number of arguments 82 00:04:35,850 --> 00:04:37,080 and returns something else. 83 00:04:37,080 --> 00:04:38,580 But here we see something different. 84 00:04:38,580 --> 00:04:48,360 We see connect getPropsFromState, which is an invoked function there. 85 00:04:48,360 --> 00:04:50,460 And then we see it invoked again. 86 00:04:50,460 --> 00:04:55,320 So it suggests to us that connect actually returns some sort of function. 87 00:04:55,320 --> 00:04:57,300 Why might that be? 88 00:04:57,300 --> 00:05:02,760 So it turns out there was this movement in JavaScript where we wanted to-- 89 00:05:02,760 --> 00:05:06,450 or JavaScript, ECMAScript-- wanted to use something 90 00:05:06,450 --> 00:05:09,630 that Python developers actually use quite often, called a decorator. 91 00:05:09,630 --> 00:05:13,110 So, if you remember back to CS50, you might have seen some syntax 92 00:05:13,110 --> 00:05:17,375 like this, where its like at @connect or some function, 93 00:05:17,375 --> 00:05:19,000 and you pass it a couple of parameters. 94 00:05:19,000 --> 00:05:23,070 So maybe something like getPropsFromState. 95 00:05:23,070 --> 00:05:25,775 And then, immediately below, you see some class creations, 96 00:05:25,775 --> 00:05:29,160 so like class ContactListScreen. 97 00:05:29,160 --> 00:05:32,010 98 00:05:32,010 --> 00:05:38,510 And maybe it extends React.Component and the like. 99 00:05:38,510 --> 00:05:41,010 And so this is something that you see quite often in Python, 100 00:05:41,010 --> 00:05:47,670 where you have a higher-order function that might take some parameters. 101 00:05:47,670 --> 00:05:50,820 And then it looks to this class beyond it 102 00:05:50,820 --> 00:05:55,650 to basically wrap in whatever function that higher-order function is 103 00:05:55,650 --> 00:05:56,650 looking for. 104 00:05:56,650 --> 00:05:58,441 And so this was actually a language feature 105 00:05:58,441 --> 00:06:01,980 that was proposed into JavaScript but never really took off. 106 00:06:01,980 --> 00:06:05,400 People didn't really-- not that they didn't enjoy it, 107 00:06:05,400 --> 00:06:07,630 but the community didn't really start using it. 108 00:06:07,630 --> 00:06:11,430 And so a lot of higher-order components retained that function prototype, 109 00:06:11,430 --> 00:06:14,730 whereby you pass in some-- 110 00:06:14,730 --> 00:06:17,190 you actually invoke the function with some configuration 111 00:06:17,190 --> 00:06:20,370 and it returns another function, which we immediately 112 00:06:20,370 --> 00:06:25,500 invoke right here, with the particular class that we want wrapped by connect. 113 00:06:25,500 --> 00:06:27,450 And so even though it looks very strange, 114 00:06:27,450 --> 00:06:31,000 there is actually a reason why this prototype looks like this. 115 00:06:31,000 --> 00:06:35,700 116 00:06:35,700 --> 00:06:41,560 But this is exactly how we end up using connect in this particular function. 117 00:06:41,560 --> 00:06:45,320 We also looked at another function or another-- 118 00:06:45,320 --> 00:06:47,070 the second argument to connect is actually 119 00:06:47,070 --> 00:06:49,590 a way to bind dispatch to your action creators. 120 00:06:49,590 --> 00:06:55,440 And so we did that in the AddContactScreen, 121 00:06:55,440 --> 00:06:58,020 where we again imported connect from react-redux. 122 00:06:58,020 --> 00:07:00,910 And down here, we actually pass null for our first argument. 123 00:07:00,910 --> 00:07:04,320 And so, in the previous example, that first argument 124 00:07:04,320 --> 00:07:07,770 was a function that maps our current state to the props 125 00:07:07,770 --> 00:07:11,790 that we expect the particular class to receive. 126 00:07:11,790 --> 00:07:15,220 But in this example, we didn't actually care about any props from the store. 127 00:07:15,220 --> 00:07:21,450 Rather, we only cared about this action creator called addContact. 128 00:07:21,450 --> 00:07:25,720 And so, by passing in this object here in the second parameter, 129 00:07:25,720 --> 00:07:33,200 it actually bound that addContact call to our dispatch. 130 00:07:33,200 --> 00:07:39,270 So by invoking it with this object here, it actually dispatches it for us, 131 00:07:39,270 --> 00:07:43,950 rather than having to import the store and then using store.dispatch 132 00:07:43,950 --> 00:07:45,090 and passing that an action. 133 00:07:45,090 --> 00:07:49,240 134 00:07:49,240 --> 00:07:50,980 So that's react-redux. 135 00:07:50,980 --> 00:07:53,140 But last week we didn't actually talk about how 136 00:07:53,140 --> 00:07:55,720 to use Redux with any asynchronous action. 137 00:07:55,720 --> 00:07:57,770 So, if you recall from a couple of weeks ago, 138 00:07:57,770 --> 00:08:00,460 we actually had a bunch of different asynchronous functions 139 00:08:00,460 --> 00:08:04,280 when we wanted to use something like an API or a network request. 140 00:08:04,280 --> 00:08:09,460 So this week, we're going to extend our simpleRedux with the ability 141 00:08:09,460 --> 00:08:11,200 to handle asynchronous actions. 142 00:08:11,200 --> 00:08:14,590 143 00:08:14,590 --> 00:08:17,080 So what sort of things might we need to consider 144 00:08:17,080 --> 00:08:20,440 when we want to support async requests? 145 00:08:20,440 --> 00:08:23,160 Well, first, where do we want to add this support? 146 00:08:23,160 --> 00:08:28,840 And how do we change our API so that what is supported can work? 147 00:08:28,840 --> 00:08:30,489 Well, where can we do that? 148 00:08:30,489 --> 00:08:32,030 So there are three different places-- 149 00:08:32,030 --> 00:08:34,570 our reducers, our store, and our actions. 150 00:08:34,570 --> 00:08:37,960 And so, if you recall from last week, Redux 151 00:08:37,960 --> 00:08:42,990 is a cycle where you start with something like an action. 152 00:08:42,990 --> 00:08:47,950 153 00:08:47,950 --> 00:08:57,730 It goes through this thing called a reducer, whereby the reducer will 154 00:08:57,730 --> 00:08:59,500 decide exactly what to do with that action 155 00:08:59,500 --> 00:09:02,035 and how it might want to change the centralized store. 156 00:09:02,035 --> 00:09:08,490 157 00:09:08,490 --> 00:09:14,420 And, from the store, any views will update. 158 00:09:14,420 --> 00:09:18,680 159 00:09:18,680 --> 00:09:22,740 And then, from those views, we might kick off another action. 160 00:09:22,740 --> 00:09:25,160 And so, in this model, we're going have to extend it 161 00:09:25,160 --> 00:09:28,439 with some sort of asynchronous ability. 162 00:09:28,439 --> 00:09:29,730 Where might we want to do that? 163 00:09:29,730 --> 00:09:37,620 164 00:09:37,620 --> 00:09:41,600 Well, if we did it in our reducer, what problems will we run into? 165 00:09:41,600 --> 00:09:44,270 We might run into this thing where our reducer actually 166 00:09:44,270 --> 00:09:47,320 ends up using an old value of the state when it wants to update. 167 00:09:47,320 --> 00:09:50,630 And so it might not be a good idea to introduce anything asynchronous 168 00:09:50,630 --> 00:09:51,590 in there. 169 00:09:51,590 --> 00:09:54,530 As you recall from last week, when we talked about reducers, 170 00:09:54,530 --> 00:09:57,830 everything in the reducer should be very pure. 171 00:09:57,830 --> 00:10:01,430 And so, by waiting for asynchronous calls to come back, 172 00:10:01,430 --> 00:10:03,830 we kind of break that guarantee of purity, 173 00:10:03,830 --> 00:10:09,020 where if we have a call that comes back after another action has been 174 00:10:09,020 --> 00:10:11,990 dispatched, then we might be using an invalid value 175 00:10:11,990 --> 00:10:15,760 for our current state and our reducer. 176 00:10:15,760 --> 00:10:19,227 And so should we rather, then, do it in the store? 177 00:10:19,227 --> 00:10:21,560 Well, I'm not sure it makes sense to do it in the store, 178 00:10:21,560 --> 00:10:25,650 because the store, all it does is it tracks the current state of our app. 179 00:10:25,650 --> 00:10:29,300 And so it doesn't really make sense to add any sort of asynchronous things 180 00:10:29,300 --> 00:10:31,130 to that. 181 00:10:31,130 --> 00:10:31,670 What's left? 182 00:10:31,670 --> 00:10:32,210 Actions. 183 00:10:32,210 --> 00:10:34,650 Can we add anything to our actions? 184 00:10:34,650 --> 00:10:35,510 So what are actions? 185 00:10:35,510 --> 00:10:38,630 Actions are just basically objects with a type that 186 00:10:38,630 --> 00:10:40,760 lets us know exactly what type of action it is, 187 00:10:40,760 --> 00:10:45,440 and maybe a payload to know exactly what we want to update our store with. 188 00:10:45,440 --> 00:10:49,597 So it also doesn't really make sense to add anything asynchronous to there. 189 00:10:49,597 --> 00:10:50,930 And so what might we do instead? 190 00:10:50,930 --> 00:10:53,572 191 00:10:53,572 --> 00:10:55,280 Well, if you recall, we have these things 192 00:10:55,280 --> 00:10:58,630 called action creators, which are functions that return actions. 193 00:10:58,630 --> 00:11:01,970 And it might actually make sense to add some asynchronous things to there, 194 00:11:01,970 --> 00:11:06,850 whereby an action creator, rather than just returning a single action, 195 00:11:06,850 --> 00:11:08,660 maybe it will return multiple actions. 196 00:11:08,660 --> 00:11:11,300 Maybe it will kick off an asynchronous request 197 00:11:11,300 --> 00:11:19,130 and then start dispatching actions as those asynchronous requests come back. 198 00:11:19,130 --> 00:11:22,640 So how might we be able to do that? 199 00:11:22,640 --> 00:11:25,190 Well, certainly we'd have to change more than just the action 200 00:11:25,190 --> 00:11:28,100 creators, because how do action creators, 201 00:11:28,100 --> 00:11:30,560 how do the actions actually get dispatched? 202 00:11:30,560 --> 00:11:33,170 It's by the store.dispatch function. 203 00:11:33,170 --> 00:11:38,000 And so, if we're doing multiple actions in our action creators, 204 00:11:38,000 --> 00:11:40,760 we might need to extend our dispatch function 205 00:11:40,760 --> 00:11:44,310 to handle those functions as well. 206 00:11:44,310 --> 00:11:50,810 So store.dispatch needs to accept other types, types other than just actions. 207 00:11:50,810 --> 00:11:58,130 So let's check our implementation of simpleRedux 208 00:11:58,130 --> 00:12:01,149 So this file is copied exactly from last week, and if we want to review 209 00:12:01,149 --> 00:12:02,940 we can just take a quick look through this. 210 00:12:02,940 --> 00:12:07,800 So in the first couple of lines, we declare a couple action types. 211 00:12:07,800 --> 00:12:10,640 And if you recall, action types are just that key 212 00:12:10,640 --> 00:12:14,660 in an action that lets us know exactly what the action type is. 213 00:12:14,660 --> 00:12:16,550 And the reason that we created these types, 214 00:12:16,550 --> 00:12:21,500 rather than just having an action hard code their exact type, 215 00:12:21,500 --> 00:12:26,030 is just to save us from typos, since we use these types to mark actions 216 00:12:26,030 --> 00:12:27,920 as a particular kind of action. 217 00:12:27,920 --> 00:12:34,685 And we also use these action types to check in our reducer against what 218 00:12:34,685 --> 00:12:36,170 the action type may be. 219 00:12:36,170 --> 00:12:38,660 And so by abstracting these types out, it kind of 220 00:12:38,660 --> 00:12:42,540 gives us the guarantee that we don't accidentally have a typo in there 221 00:12:42,540 --> 00:12:44,570 or something like that. 222 00:12:44,570 --> 00:12:47,270 And then we went and implemented our store. 223 00:12:47,270 --> 00:12:50,370 And so our store, when you create it, is passed a couple of things. 224 00:12:50,370 --> 00:12:52,491 One is the reducer, and one is the initial state. 225 00:12:52,491 --> 00:12:55,240 And so we just go ahead and store those things in the constructor. 226 00:12:55,240 --> 00:12:59,150 That way, when something like store.getState or store.dispatch 227 00:12:59,150 --> 00:13:03,240 is invoked, we remember those arguments. 228 00:13:03,240 --> 00:13:06,570 So first, we implemented store.getState, which was pretty simple. 229 00:13:06,570 --> 00:13:09,740 We just returned whatever the current state of our store is. 230 00:13:09,740 --> 00:13:13,340 And then we implemented this thing called store.dispatch, 231 00:13:13,340 --> 00:13:15,499 where we passed it some sort of update. 232 00:13:15,499 --> 00:13:16,290 And what did we do? 233 00:13:16,290 --> 00:13:21,080 Well, we said, hey, whatever reducer you ended up creating me with, 234 00:13:21,080 --> 00:13:24,980 just pass the reducer the current state of our store and any update 235 00:13:24,980 --> 00:13:28,850 that we wanted to update this store with. 236 00:13:28,850 --> 00:13:33,320 So pretty simple store implementation. 237 00:13:33,320 --> 00:13:37,040 Then down here, we wrote our reducers. 238 00:13:37,040 --> 00:13:40,580 So line 20, we just declared a default state. 239 00:13:40,580 --> 00:13:43,790 Line 22, we had a helper function called merge. 240 00:13:43,790 --> 00:13:46,550 And then we implemented our three reducers. 241 00:13:46,550 --> 00:13:50,589 So we had our main reducer, which takes a state and an action, just 242 00:13:50,589 --> 00:13:51,630 like every other reducer. 243 00:13:51,630 --> 00:13:54,620 So every single reducer has the same function prototype. 244 00:13:54,620 --> 00:13:58,531 We expect a state or the previous state, an action. 245 00:13:58,531 --> 00:14:00,530 And we want to return whatever the new state is. 246 00:14:00,530 --> 00:14:05,900 And so our main reducer, what it did is it just dispatched 247 00:14:05,900 --> 00:14:06,980 to our other reducers. 248 00:14:06,980 --> 00:14:11,840 And so it said, hey, the user key in our main application state 249 00:14:11,840 --> 00:14:14,330 is actually handled by our userReducer. 250 00:14:14,330 --> 00:14:18,470 And so let me just pass the user part of the state 251 00:14:18,470 --> 00:14:20,450 along with the action to the userReducer, 252 00:14:20,450 --> 00:14:24,467 and we'll just set the user key to whatever that happens to return. 253 00:14:24,467 --> 00:14:26,300 And we did the same thing with the contacts. 254 00:14:26,300 --> 00:14:29,930 And so, up here, we actually implemented those. 255 00:14:29,930 --> 00:14:33,440 And so our contactReducer took the current state. 256 00:14:33,440 --> 00:14:37,200 And keep in mind, this state and this state here are different. 257 00:14:37,200 --> 00:14:41,012 This one actually refers to only the contacts key in that state. 258 00:14:41,012 --> 00:14:42,470 And, again, it got the same action. 259 00:14:42,470 --> 00:14:45,560 And if the action was something like updateContact, what we did 260 00:14:45,560 --> 00:14:50,220 is we just returned an array with the new contact appended to the end. 261 00:14:50,220 --> 00:14:52,220 And if it was an action other than that, we just 262 00:14:52,220 --> 00:14:56,710 said, hey, the state in this particular part of the store remains unchanged. 263 00:14:56,710 --> 00:15:01,010 In the userReducer, we did a very similar thing. 264 00:15:01,010 --> 00:15:03,080 If we want to update the user, what we're doing 265 00:15:03,080 --> 00:15:07,990 is we're just going to merge into the state the action.payload. 266 00:15:07,990 --> 00:15:10,010 And as a just quick example, if we wanted 267 00:15:10,010 --> 00:15:16,610 to update contact, or the same action as we did in the contactReducer, 268 00:15:16,610 --> 00:15:19,700 we just store something in our user state 269 00:15:19,700 --> 00:15:22,090 as well, just keeping track of the previous contact. 270 00:15:22,090 --> 00:15:23,810 That didn't really have any significance to our app. 271 00:15:23,810 --> 00:15:26,720 We just wanted to demonstrate that our two reducers could actually 272 00:15:26,720 --> 00:15:30,260 handle the same exact action type. 273 00:15:30,260 --> 00:15:33,620 And then, lastly, we started defining our action creators, which 274 00:15:33,620 --> 00:15:38,810 were the things that get passed some actual updates 275 00:15:38,810 --> 00:15:42,300 and will return exactly the shape of the action that we wanted. 276 00:15:42,300 --> 00:15:46,580 And so we had one that was called updateUser, which took an update. 277 00:15:46,580 --> 00:15:50,000 And so whatever the updated user, or whatever 278 00:15:50,000 --> 00:15:54,470 the updated object we want the user to be, we just pass it in here. 279 00:15:54,470 --> 00:15:59,030 And then we output a Flux standard action, or an action 280 00:15:59,030 --> 00:16:00,620 that has a type and a payload. 281 00:16:00,620 --> 00:16:02,857 And the payload just happens to be the update. 282 00:16:02,857 --> 00:16:04,940 Again, we do a very similar thing with addContact, 283 00:16:04,940 --> 00:16:08,196 where we take the new contact, we create a Flex standard action 284 00:16:08,196 --> 00:16:10,070 with the type UPDATE_CONTACT, and the payload 285 00:16:10,070 --> 00:16:12,260 is just whatever the new contact is. 286 00:16:12,260 --> 00:16:14,420 And then, down here, we wrote some tests to make 287 00:16:14,420 --> 00:16:17,480 sure our store does exactly what we expected it to do. 288 00:16:17,480 --> 00:16:20,960 So we created a new store with the reducer and our default state. 289 00:16:20,960 --> 00:16:22,880 We dispatched a few actions. 290 00:16:22,880 --> 00:16:27,050 And then we just logged our store.getState. 291 00:16:27,050 --> 00:16:31,430 And at the very end, when we run this, we 292 00:16:31,430 --> 00:16:34,460 are output with the current state of our app after all of those 293 00:16:34,460 --> 00:16:36,924 dispatched actions. 294 00:16:36,924 --> 00:16:38,840 So lots of stuff jammed into that single file, 295 00:16:38,840 --> 00:16:40,280 but we were missing one key thing. 296 00:16:40,280 --> 00:16:43,910 We were missing a way to support those asynchronous actions. 297 00:16:43,910 --> 00:16:50,900 And so let's go ahead and copy this file into a file called store3.js, 298 00:16:50,900 --> 00:16:55,220 and in this file we're going to go ahead and implement a way 299 00:16:55,220 --> 00:16:58,720 to handle asynchronous actions. 300 00:16:58,720 --> 00:17:01,780 And so, in this slide, we discussed and determined 301 00:17:01,780 --> 00:17:07,119 that the best way to do this would be to change our action creators such 302 00:17:07,119 --> 00:17:13,599 that the action creators can do some logic for sending out a network request 303 00:17:13,599 --> 00:17:18,319 and dispatch multiple actions as this happens. 304 00:17:18,319 --> 00:17:21,760 And so, down here, we have a few action creators. 305 00:17:21,760 --> 00:17:26,680 We have something like updateUser and addContact, but both of them 306 00:17:26,680 --> 00:17:29,330 just immediately return a new object. 307 00:17:29,330 --> 00:17:38,420 So let's actually try to flesh out a way that we can do something like logInUser 308 00:17:38,420 --> 00:17:42,541 such that it handles some sort of asynchronous network request. 309 00:17:42,541 --> 00:17:44,540 And so let's just leave a comment here that lets 310 00:17:44,540 --> 00:17:47,960 us know that this is going to be an asynchronous action creator. 311 00:17:47,960 --> 00:17:50,540 312 00:17:50,540 --> 00:17:52,160 So how might we do that? 313 00:17:52,160 --> 00:17:57,150 In the past, all we did is just immediately return an object. 314 00:17:57,150 --> 00:17:59,930 So we'll probably want to do that, so eventually we're 315 00:17:59,930 --> 00:18:04,220 going to want to at least-- 316 00:18:04,220 --> 00:18:07,640 for now, let's just write return something 317 00:18:07,640 --> 00:18:16,872 like type LOG_IN_SUCCESS or something. 318 00:18:16,872 --> 00:18:21,030 319 00:18:21,030 --> 00:18:22,040 So this works. 320 00:18:22,040 --> 00:18:27,390 We can go ahead and do something like invoke logInUser. 321 00:18:27,390 --> 00:18:35,000 And if we store.dispatch that, then it will correctly dispatch the action, 322 00:18:35,000 --> 00:18:38,080 as long as it's declared after store. 323 00:18:38,080 --> 00:18:40,080 But, again, that doesn't really do what we want. 324 00:18:40,080 --> 00:18:42,170 We want something asynchronous. 325 00:18:42,170 --> 00:18:52,370 And so maybe we should do something like fetch something and then do a .then 326 00:18:52,370 --> 00:19:03,325 return this exact object. 327 00:19:03,325 --> 00:19:06,820 328 00:19:06,820 --> 00:19:08,780 But this actually doesn't work either. 329 00:19:08,780 --> 00:19:15,610 All this does is it returns to the next .then statement in our promise chain. 330 00:19:15,610 --> 00:19:21,040 So what we actually have to do is figure out a way to dispatch these actions. 331 00:19:21,040 --> 00:19:28,825 And so what if we expected something to pass in something like dispatch? 332 00:19:28,825 --> 00:19:31,750 333 00:19:31,750 --> 00:19:35,600 So, again, now we have a completely new function prototype. 334 00:19:35,600 --> 00:19:40,870 And so when we invoke something like logInUser, 335 00:19:40,870 --> 00:19:43,360 this actually returns a function. 336 00:19:43,360 --> 00:19:45,130 And what function is that? 337 00:19:45,130 --> 00:19:49,840 It expects a dispatch, and then it does some logic. 338 00:19:49,840 --> 00:19:51,410 And so we'll come back to that later. 339 00:19:51,410 --> 00:19:56,607 But let's just assume that this dispatch here is the dispatch that we want. 340 00:19:56,607 --> 00:19:57,940 And so what might we want to do? 341 00:19:57,940 --> 00:20:03,170 Maybe first we will dispatch an action that says, hey, 342 00:20:03,170 --> 00:20:05,410 I'm about to send an asynchronous request. 343 00:20:05,410 --> 00:20:10,704 And maybe that type is LOG_IN_SENT. 344 00:20:10,704 --> 00:20:15,490 345 00:20:15,490 --> 00:20:17,890 And we can rename that later. 346 00:20:17,890 --> 00:20:18,760 So great. 347 00:20:18,760 --> 00:20:26,630 This effectively does, if we comment out this part, 348 00:20:26,630 --> 00:20:32,240 this effectively does what our other functions are doing. 349 00:20:32,240 --> 00:20:35,660 Our other action creators immediately return an object, 350 00:20:35,660 --> 00:20:39,180 which is dispatched down here. 351 00:20:39,180 --> 00:20:41,960 But what this asynchronous action creator does 352 00:20:41,960 --> 00:20:44,630 is it expects us to pass it a dispatch, and then it 353 00:20:44,630 --> 00:20:46,310 will handle the dispatching itself. 354 00:20:46,310 --> 00:20:52,820 And so we're going to have to add support to our store in order for it 355 00:20:52,820 --> 00:20:56,360 to know that it needs to pass the dispatch into our new action creators. 356 00:20:56,360 --> 00:20:59,561 And so we'll add some logic for that in a bit. 357 00:20:59,561 --> 00:21:01,810 But maybe after this it'll send off the fetch request. 358 00:21:01,810 --> 00:21:04,460 359 00:21:04,460 --> 00:21:16,100 And then, when it comes back, maybe it will dispatch that new action creator. 360 00:21:16,100 --> 00:21:18,335 And this dispatch is the same as this dispatch, 361 00:21:18,335 --> 00:21:20,210 and so it will go ahead and actually dispatch 362 00:21:20,210 --> 00:21:22,313 that action completely asynchronously. 363 00:21:22,313 --> 00:21:26,390 364 00:21:26,390 --> 00:21:31,480 And maybe we would want to catch any errors. 365 00:21:31,480 --> 00:21:35,500 And if there is an error, maybe we should dispatch something 366 00:21:35,500 --> 00:21:38,980 like LOG_IN_REJECTED. 367 00:21:38,980 --> 00:21:46,770 368 00:21:46,770 --> 00:21:50,430 And now we have what is starting to resemble an asynchronous action 369 00:21:50,430 --> 00:21:51,150 creator. 370 00:21:51,150 --> 00:21:56,370 And so it's a function that takes currently no arguments. 371 00:21:56,370 --> 00:22:00,180 It returns a function that expects us to let it know exactly how it 372 00:22:00,180 --> 00:22:02,670 should be dispatching these actions. 373 00:22:02,670 --> 00:22:06,570 And then it goes ahead and actually dispatches an action here. 374 00:22:06,570 --> 00:22:08,550 And then it sends a fetch request. 375 00:22:08,550 --> 00:22:10,440 And when it comes back asynchronously, it 376 00:22:10,440 --> 00:22:14,010 knows how to dispatch another action, and so it can go ahead and dispatch 377 00:22:14,010 --> 00:22:15,690 a new action for us. 378 00:22:15,690 --> 00:22:19,740 And maybe if the fetch errors or maybe it gets rejected, 379 00:22:19,740 --> 00:22:24,240 we know exactly how to dispatch a new action that's letting our app 380 00:22:24,240 --> 00:22:28,050 know that that login was rejected. 381 00:22:28,050 --> 00:22:32,120 So this is exactly what an asynchronous action creator looks like. 382 00:22:32,120 --> 00:22:34,742 But now we just need to add support. 383 00:22:34,742 --> 00:22:36,950 We need to change more than just the action creators. 384 00:22:36,950 --> 00:22:39,440 We need to change our store.dispatch so that it 385 00:22:39,440 --> 00:22:45,740 knows that it should be letting our async action creator know 386 00:22:45,740 --> 00:22:49,310 how to dispatch its own actions. 387 00:22:49,310 --> 00:22:50,310 So how might we do that? 388 00:22:50,310 --> 00:22:53,850 389 00:22:53,850 --> 00:22:56,510 Well, currently in our dispatch function, we take an update 390 00:22:56,510 --> 00:22:57,676 and pass it to our reducers. 391 00:22:57,676 --> 00:23:01,100 And so let's actually change the name of this argument to something like action. 392 00:23:01,100 --> 00:23:04,780 393 00:23:04,780 --> 00:23:07,690 So it takes an action and immediately resets the state 394 00:23:07,690 --> 00:23:10,570 to the return value of our reducer. 395 00:23:10,570 --> 00:23:15,720 But now we need to make sure that action is actually an action. 396 00:23:15,720 --> 00:23:21,730 Or more, we should check and see if the action is actually a function. 397 00:23:21,730 --> 00:23:23,830 So let's just do this. 398 00:23:23,830 --> 00:23:29,762 If the type of the action is a function, we're 399 00:23:29,762 --> 00:23:31,220 going to want to do something else. 400 00:23:31,220 --> 00:23:33,850 401 00:23:33,850 --> 00:23:37,900 And if it's not, then we're going to do what we did before 402 00:23:37,900 --> 00:23:42,880 and just assume that the action is an action and pass it to our reducer. 403 00:23:42,880 --> 00:23:47,030 But what are we going to do if the action is a function? 404 00:23:47,030 --> 00:23:51,280 Well, if the action is a function, it wants 405 00:23:51,280 --> 00:23:54,320 to know how to dispatch its own actions. 406 00:23:54,320 --> 00:24:01,860 And so we should actually pass to the action the way to dispatch, 407 00:24:01,860 --> 00:24:03,000 so this.dispatch. 408 00:24:03,000 --> 00:24:06,370 409 00:24:06,370 --> 00:24:09,820 And then, once it knows how to dispatch, it 410 00:24:09,820 --> 00:24:12,010 can then take care of dispatching its own actions 411 00:24:12,010 --> 00:24:15,870 and do that completely asynchronously. 412 00:24:15,870 --> 00:24:19,180 There is a small bug here. 413 00:24:19,180 --> 00:24:23,070 So if we pass something like this.dispatch and dispatch 414 00:24:23,070 --> 00:24:30,990 is then invoked in some other context, then we lose the correct this binding. 415 00:24:30,990 --> 00:24:33,780 And so we've run into this bug many times throughout this course, 416 00:24:33,780 --> 00:24:37,230 and we know how to fix that. 417 00:24:37,230 --> 00:24:39,980 We could just make dispatch a class property. 418 00:24:39,980 --> 00:24:42,900 419 00:24:42,900 --> 00:24:47,220 That automatically takes care of the this binding for us. 420 00:24:47,220 --> 00:24:50,579 But, unfortunately, that doesn't work in Node. 421 00:24:50,579 --> 00:24:52,620 And since we're going to try to run this in Node, 422 00:24:52,620 --> 00:24:55,500 we're going to have to do something else. 423 00:24:55,500 --> 00:25:06,420 We can just do .bind here and explicitly bind it to the this context when we run 424 00:25:06,420 --> 00:25:08,860 this function. 425 00:25:08,860 --> 00:25:09,360 Cool. 426 00:25:09,360 --> 00:25:10,570 So we're pretty much there. 427 00:25:10,570 --> 00:25:15,570 And let's just go ahead and finish this action creator. 428 00:25:15,570 --> 00:25:18,210 And so logInUser should actually take something 429 00:25:18,210 --> 00:25:22,170 like a username and a password, because you need those two things in order 430 00:25:22,170 --> 00:25:25,160 to log in a user. 431 00:25:25,160 --> 00:25:29,620 Then we're going to want to fetch our actual login endpoint. 432 00:25:29,620 --> 00:25:32,910 And so, if you remember from the previous lectures, 433 00:25:32,910 --> 00:25:36,769 our login endpoint was just this authServer here. 434 00:25:36,769 --> 00:25:38,810 So let's actually go ahead and start that server. 435 00:25:38,810 --> 00:25:43,365 436 00:25:43,365 --> 00:25:44,990 So now this server is actually running. 437 00:25:44,990 --> 00:25:51,880 438 00:25:51,880 --> 00:25:54,560 In the previous lecture, we wrote a function 439 00:25:54,560 --> 00:26:00,590 that helps us abstract out the log in logic here, so let's 440 00:26:00,590 --> 00:26:06,500 actually just cut and paste this to our simpleRedux here. 441 00:26:06,500 --> 00:26:15,830 442 00:26:15,830 --> 00:26:21,140 So I just cut and paste that login function from our API module 443 00:26:21,140 --> 00:26:24,320 that we wrote a few weeks ago, which handles all of the logic for us. 444 00:26:24,320 --> 00:26:26,540 It takes in a username and a password. 445 00:26:26,540 --> 00:26:28,880 It goes ahead and fetches everything for us, 446 00:26:28,880 --> 00:26:30,920 making sure that the correct headers are set. 447 00:26:30,920 --> 00:26:33,810 It then checks if the response is what we want it to be. 448 00:26:33,810 --> 00:26:37,080 And if not, then it will throw an error. 449 00:26:37,080 --> 00:26:40,550 So we can go ahead and use that login function down here. 450 00:26:40,550 --> 00:26:44,440 Rather than just this empty fetch call, we can do login, 451 00:26:44,440 --> 00:26:46,190 and we pass it that username and password. 452 00:26:46,190 --> 00:26:55,840 453 00:26:55,840 --> 00:26:56,810 And then what do we do? 454 00:26:56,810 --> 00:26:59,949 If it was a success, then we go ahead and let 455 00:26:59,949 --> 00:27:01,490 our store know that it was a success. 456 00:27:01,490 --> 00:27:02,990 And how do we let our store know? 457 00:27:02,990 --> 00:27:06,460 Well, we dispatch an action saying that it was a success. 458 00:27:06,460 --> 00:27:08,460 And if it was an error, then what do we do? 459 00:27:08,460 --> 00:27:11,240 Well, we let our store know that it was an error 460 00:27:11,240 --> 00:27:17,840 by dispatching an action that just lets us know that the login was rejected. 461 00:27:17,840 --> 00:27:20,941 462 00:27:20,941 --> 00:27:21,440 Great. 463 00:27:21,440 --> 00:27:23,030 So let's actually try running this. 464 00:27:23,030 --> 00:27:25,730 465 00:27:25,730 --> 00:27:32,710 So let's comment out all of this old dispatching, 466 00:27:32,710 --> 00:27:36,104 and instead do store.dispatch. 467 00:27:36,104 --> 00:27:37,270 And what are we dispatching? 468 00:27:37,270 --> 00:27:42,580 Well, we're going to dispatch this action creator called logInUser. 469 00:27:42,580 --> 00:27:47,680 And we're going to pass it the username and the password 470 00:27:47,680 --> 00:27:50,080 that we know are correct. 471 00:27:50,080 --> 00:27:51,460 And what do we expect to happen? 472 00:27:51,460 --> 00:27:56,230 Well, we first expect it to dispatch an action that 473 00:27:56,230 --> 00:27:59,140 lets our reducer know that the login was sent. 474 00:27:59,140 --> 00:28:02,260 We then want to dispatch an action that lets 475 00:28:02,260 --> 00:28:07,330 our store know that it was a success. 476 00:28:07,330 --> 00:28:09,580 And if not, then we want to dispatch an action that 477 00:28:09,580 --> 00:28:12,700 lets us know that it was a failure. 478 00:28:12,700 --> 00:28:16,460 So let's actually add some logging so that we know that this is happening. 479 00:28:16,460 --> 00:28:19,900 So if the action is a function, then run this. 480 00:28:19,900 --> 00:28:32,840 Otherwise, let's console.log received an action, and let's log that action.type. 481 00:28:32,840 --> 00:28:46,980 482 00:28:46,980 --> 00:28:49,920 So, presumably, this is going to work, so we're just 483 00:28:49,920 --> 00:28:53,585 going to dispatch the logInUser, and then we're going to log the state. 484 00:28:53,585 --> 00:28:56,460 We don't expect the state to change because we never actually updated 485 00:28:56,460 --> 00:28:57,950 our reducer functions. 486 00:28:57,950 --> 00:29:00,720 But hopefully we're going to see a few actions that 487 00:29:00,720 --> 00:29:06,550 say something along the lines of LOG_IN_SENT and then LOG_IN_SUCCESS. 488 00:29:06,550 --> 00:29:10,170 So let's go ahead and run store3. 489 00:29:10,170 --> 00:29:15,900 And we received LOG_IN_SENT and then received LOG_IN_REJECTED. 490 00:29:15,900 --> 00:29:22,950 The reason for that is because we're using Node, 491 00:29:22,950 --> 00:29:25,830 and Node does not have any concept of what fetch is. 492 00:29:25,830 --> 00:29:27,930 Because, if you remember from previous lectures, 493 00:29:27,930 --> 00:29:31,080 we discussed that fetch was actually part of the browser API 494 00:29:31,080 --> 00:29:33,250 rather than part of the JavaScript API. 495 00:29:33,250 --> 00:29:38,790 And since we're using Node.js, which is more just-- the JavaScript in it 496 00:29:38,790 --> 00:29:42,360 doesn't have all of the browser APIs built in. 497 00:29:42,360 --> 00:29:45,240 What we can actually do is install this thing 498 00:29:45,240 --> 00:29:54,010 called isomorphic-fetch, which is just a package that implements fetch for us, 499 00:29:54,010 --> 00:29:57,690 that we can then use in a Node.js environment. 500 00:29:57,690 --> 00:30:11,690 And so let's again try this file and do this require statement, which 501 00:30:11,690 --> 00:30:14,510 is Node's version of an import. 502 00:30:14,510 --> 00:30:25,060 Then now we can run this again and see that the login was indeed a success. 503 00:30:25,060 --> 00:30:25,560 Great. 504 00:30:25,560 --> 00:30:32,040 So now we went and actually implemented our own asynchronous action creator. 505 00:30:32,040 --> 00:30:40,860 And we can go ahead, if we want to add something to our reducer, 506 00:30:40,860 --> 00:30:46,470 or userReducer, that says, if the action type is LOG_IN_SUCCESS, 507 00:30:46,470 --> 00:30:49,520 then we can just maybe store some sort of token. 508 00:30:49,520 --> 00:30:51,900 So let's go ahead and actually implement that. 509 00:30:51,900 --> 00:31:01,970 So we could do, if action.type is LOG_IN_SUCCESS, then do something. 510 00:31:01,970 --> 00:31:04,900 But now we're starting to get a little bit unnecessarily terse 511 00:31:04,900 --> 00:31:07,760 in here, unnecessarily repeating ourself here. 512 00:31:07,760 --> 00:31:10,940 So we have action.type, action.type, action.type. 513 00:31:10,940 --> 00:31:14,750 And we can actually use something built into JavaScript called a switch 514 00:31:14,750 --> 00:31:23,310 statement, which is a cleaner way of checking against the same value 515 00:31:23,310 --> 00:31:24,730 over and over and over. 516 00:31:24,730 --> 00:31:30,810 And so we can say, if action.type happens to be UPDATE_USER, 517 00:31:30,810 --> 00:31:34,350 then return what we're going to return here. 518 00:31:34,350 --> 00:31:36,920 519 00:31:36,920 --> 00:31:43,720 In the case where it's UPDATE_CONTACT, we're going to return this here. 520 00:31:43,720 --> 00:31:46,500 521 00:31:46,500 --> 00:31:54,350 And if the case is LOG_IN_SUCCESS, then we 522 00:31:54,350 --> 00:32:04,521 can return merge the state with something like token, some fake token 523 00:32:04,521 --> 00:32:05,770 that we're going to hard code. 524 00:32:05,770 --> 00:32:10,900 525 00:32:10,900 --> 00:32:15,670 And, by default, if it isn't any of the types that we're looking for, 526 00:32:15,670 --> 00:32:19,790 just return the state unchanged. 527 00:32:19,790 --> 00:32:21,580 And now we have our userReducer. 528 00:32:21,580 --> 00:32:25,080 529 00:32:25,080 --> 00:32:31,640 And now, if we run store3, it's an asynchronous action 530 00:32:31,640 --> 00:32:34,230 so this is actually a bit messy. 531 00:32:34,230 --> 00:32:38,150 So when we do store.getState here, we still 532 00:32:38,150 --> 00:32:40,430 haven't received the asynchronous response back 533 00:32:40,430 --> 00:32:45,330 from the action over here. 534 00:32:45,330 --> 00:32:51,920 So if we wanted to actually log state at the time that it gets added, 535 00:32:51,920 --> 00:32:56,210 we can either add that to our reducer or add it to our dispatch here. 536 00:32:56,210 --> 00:33:01,507 Or we could actually add that into our Redux implementation. 537 00:33:01,507 --> 00:33:04,340 But now, if we want to add a bunch of these things, what we're doing 538 00:33:04,340 --> 00:33:07,400 is we're messing with Redux itself, and that's probably not something 539 00:33:07,400 --> 00:33:09,290 that every single consumer should do. 540 00:33:09,290 --> 00:33:11,570 So not everybody who wants to use this library 541 00:33:11,570 --> 00:33:16,010 called Redux should have to change the implementation. 542 00:33:16,010 --> 00:33:18,920 And so our additions here have been somewhat ideal 543 00:33:18,920 --> 00:33:22,310 because we're actually changing the implementation of Redux, 544 00:33:22,310 --> 00:33:26,780 and we don't want to do that every time we want to add something. 545 00:33:26,780 --> 00:33:31,310 So it turns out, built into Redux is this concept of Redux middleware, which 546 00:33:31,310 --> 00:33:33,290 allows us to extend Redux without actually 547 00:33:33,290 --> 00:33:36,500 having to mess with the implementation like we just did. 548 00:33:36,500 --> 00:33:38,750 So any function can actually be middleware. 549 00:33:38,750 --> 00:33:42,500 And what happens here is that these functions, when a new action is 550 00:33:42,500 --> 00:33:45,500 dispatched, it passes through all of these different middlewares 551 00:33:45,500 --> 00:33:47,532 before actually being dispatched. 552 00:33:47,532 --> 00:33:49,490 And so any function can be a middleware as long 553 00:33:49,490 --> 00:33:54,050 as it fits a particular function prototype, and that prototype is this. 554 00:33:54,050 --> 00:33:57,910 We first expect an object that has a couple of keys. 555 00:33:57,910 --> 00:34:00,380 One is getState, and one is dispatch. 556 00:34:00,380 --> 00:34:05,300 We then return a function that expects an argument called 557 00:34:05,300 --> 00:34:09,469 next, which is basically the next middleware in the chain. 558 00:34:09,469 --> 00:34:13,460 We then return a function that expects as an argument an action, which 559 00:34:13,460 --> 00:34:16,427 is basically every single action that gets dispatched. 560 00:34:16,427 --> 00:34:18,260 And, finally, we do whatever we want, and we 561 00:34:18,260 --> 00:34:21,679 don't have to return something at the end if we don't want to. 562 00:34:21,679 --> 00:34:25,230 So we can actually re-implement our feature as middleware. 563 00:34:25,230 --> 00:34:28,560 And so let's go ahead and do that. 564 00:34:28,560 --> 00:34:35,389 So we have a lot of logic here, and let's remove 565 00:34:35,389 --> 00:34:37,670 what we actually added to Redux itself. 566 00:34:37,670 --> 00:34:42,380 So we added this thing here that changed the way that we dispatched. 567 00:34:42,380 --> 00:34:45,590 And let's actually do that in our real Redux store. 568 00:34:45,590 --> 00:34:50,540 So let's leave our simpleRedux implementation and change directories 569 00:34:50,540 --> 00:34:53,780 into our actual directory where we use Redux. 570 00:34:53,780 --> 00:34:56,659 571 00:34:56,659 --> 00:35:00,020 So let's look in this file that we call store.js. 572 00:35:00,020 --> 00:35:02,420 So, last week, we wrote this file and it actually 573 00:35:02,420 --> 00:35:08,630 creates a store from a reducer, dispatches a few actions, console log 574 00:35:08,630 --> 00:35:10,940 store.getState, and then exports that store. 575 00:35:10,940 --> 00:35:13,940 So it's basically the same thing that we did in our other store file 576 00:35:13,940 --> 00:35:19,110 but in real Redux rather than in our simpleRedux implementation. 577 00:35:19,110 --> 00:35:24,075 So now let's actually do this thing called-- 578 00:35:24,075 --> 00:35:27,630 579 00:35:27,630 --> 00:35:30,620 let's just call it thunk. 580 00:35:30,620 --> 00:35:34,180 581 00:35:34,180 --> 00:35:38,680 And what a thunk is, is it's a way to wrap an expression 582 00:35:38,680 --> 00:35:42,910 to delay its evaluation. 583 00:35:42,910 --> 00:35:46,780 So, basically, what we're doing is, rather than returning an object, 584 00:35:46,780 --> 00:35:49,000 we're going to delay the return of an object 585 00:35:49,000 --> 00:35:52,870 by instead returning a function that can be invoked at any other time. 586 00:35:52,870 --> 00:35:59,270 And so let's actually just copy this exact function prototype. 587 00:35:59,270 --> 00:36:06,880 And so it's going to be something with keys getState and dispatch. 588 00:36:06,880 --> 00:36:08,860 Or let's just call that store. 589 00:36:08,860 --> 00:36:12,520 It's going to then return a function that expects next. 590 00:36:12,520 --> 00:36:15,820 It's then going to return a function that expects an action. 591 00:36:15,820 --> 00:36:17,365 And then it's going to do something. 592 00:36:17,365 --> 00:36:20,620 593 00:36:20,620 --> 00:36:22,390 And so what are we going to do here? 594 00:36:22,390 --> 00:36:25,290 And so, in our particular example, what we ended up doing is, 595 00:36:25,290 --> 00:36:33,840 if the type of the action is a function, then we're going to do something. 596 00:36:33,840 --> 00:36:37,390 Otherwise, we know that the action's an actual action, 597 00:36:37,390 --> 00:36:40,510 and we can just pass it on to the next middleware in the chain 598 00:36:40,510 --> 00:36:47,150 by literally passing the action to the next in the chain. 599 00:36:47,150 --> 00:36:49,990 And so the reason that these arguments are 600 00:36:49,990 --> 00:36:54,100 named next and action is because action is the actual action that was getting 601 00:36:54,100 --> 00:36:57,610 dispatched, and next is just whatever's next in the middleware chain, 602 00:36:57,610 --> 00:37:00,100 and it might actually be dispatch. 603 00:37:00,100 --> 00:37:03,192 And so if the action that we receive is a real action, 604 00:37:03,192 --> 00:37:05,650 we're just going to say, hey, we don't need to do anything. 605 00:37:05,650 --> 00:37:08,650 Just pass this action to whatever's next in the chain. 606 00:37:08,650 --> 00:37:11,290 607 00:37:11,290 --> 00:37:13,950 Otherwise, if it's a function, what do we want to do? 608 00:37:13,950 --> 00:37:18,160 Well, we want to pass our action a couple of things. 609 00:37:18,160 --> 00:37:23,960 So we could do store.dispatch, and store.getState If we want, 610 00:37:23,960 --> 00:37:28,520 but for now let's just pass the dispatch. 611 00:37:28,520 --> 00:37:37,470 And so now we can use what's called applyMiddleware, 612 00:37:37,470 --> 00:37:39,050 which is something built into Redux. 613 00:37:39,050 --> 00:37:41,630 And what it does is it lets Redux know that it should 614 00:37:41,630 --> 00:37:44,530 run every single action that we get through this middleware 615 00:37:44,530 --> 00:37:47,606 that we called thunk. 616 00:37:47,606 --> 00:37:51,551 And so here, let's just pass applyMiddleware and pass it 617 00:37:51,551 --> 00:37:52,550 this thing called thunk. 618 00:37:52,550 --> 00:37:56,010 619 00:37:56,010 --> 00:37:58,300 So what exactly did we do here? 620 00:37:58,300 --> 00:37:59,726 So in our previous-- 621 00:37:59,726 --> 00:38:03,060 622 00:38:03,060 --> 00:38:09,450 in simpleRedux, in order to support the case where our action might actually 623 00:38:09,450 --> 00:38:11,310 be a function-- 624 00:38:11,310 --> 00:38:13,970 or in other words, the thing returned by our action creator 625 00:38:13,970 --> 00:38:18,140 is a function that wants to know how to dispatch itself-- 626 00:38:18,140 --> 00:38:22,900 we added to Redux a way to know that, to notice that. 627 00:38:22,900 --> 00:38:25,920 So if the action is actually a function, then 628 00:38:25,920 --> 00:38:28,050 let it know how to dispatch its own actions. 629 00:38:28,050 --> 00:38:30,750 We don't have to dispatch the action for it. 630 00:38:30,750 --> 00:38:33,390 But if it's not, then we're just going to do our normal thing 631 00:38:33,390 --> 00:38:37,959 and handle the action how we would have done before 632 00:38:37,959 --> 00:38:41,250 But, again, that required us to actually mess with the implementation of Redux. 633 00:38:41,250 --> 00:38:48,280 And so the way Redux actually works is as follows. 634 00:38:48,280 --> 00:38:50,100 So you get an action. 635 00:38:50,100 --> 00:38:53,810 And before it's passed to reducer, we actually have an opportunity 636 00:38:53,810 --> 00:38:54,810 to mess with the action. 637 00:38:54,810 --> 00:39:00,580 638 00:39:00,580 --> 00:39:02,720 And what that is, is it's called middleware. 639 00:39:02,720 --> 00:39:08,000 640 00:39:08,000 --> 00:39:11,060 And what middleware is, is it's a chain by which we can just 641 00:39:11,060 --> 00:39:15,060 keep passing the action down the chain and modify it however we want. 642 00:39:15,060 --> 00:39:17,930 And maybe by modifying it, we just ignore it. 643 00:39:17,930 --> 00:39:19,430 We don't even dispatch a new action. 644 00:39:19,430 --> 00:39:22,130 Instead, maybe we will just say, hey action, 645 00:39:22,130 --> 00:39:24,710 you can dispatch your own things if we just pass it 646 00:39:24,710 --> 00:39:26,600 this thing called dispatch to you. 647 00:39:26,600 --> 00:39:29,110 And so middleware is actually a chain of a bunch of things. 648 00:39:29,110 --> 00:39:33,050 We could have our thing called thunk. 649 00:39:33,050 --> 00:39:35,390 If we wanted to log all of our asynchronous actions, 650 00:39:35,390 --> 00:39:41,940 maybe we'll log things as it comes through this middleware. 651 00:39:41,940 --> 00:39:46,620 And so, when you get an action, rather than going directly into the reducer, 652 00:39:46,620 --> 00:39:53,220 we have the chance to pass it into this middleware chain. 653 00:39:53,220 --> 00:39:56,970 So this action gets passed into the middleware 654 00:39:56,970 --> 00:39:59,370 through all of these functions. 655 00:39:59,370 --> 00:40:07,812 And then only here does it then get passed into the actual reducer. 656 00:40:07,812 --> 00:40:10,770 And so this concept called middleware is not something unique to Redux. 657 00:40:10,770 --> 00:40:13,630 It's actually a pattern that you see all over the place, 658 00:40:13,630 --> 00:40:17,910 including Express, which is the library that we used in order to create 659 00:40:17,910 --> 00:40:20,580 our simple authentication server. 660 00:40:20,580 --> 00:40:22,740 And so, basically, what middleware does is 661 00:40:22,740 --> 00:40:25,260 it allows us to mess with any of our parameters 662 00:40:25,260 --> 00:40:28,340 before we send those parameters into the core library. 663 00:40:28,340 --> 00:40:32,811 In this case, the core library is the reducer and the store. 664 00:40:32,811 --> 00:40:36,120 And so let's build out the rest of this example. 665 00:40:36,120 --> 00:40:44,210 666 00:40:44,210 --> 00:40:47,660 So again, what we did is we created a function called thunk middleware. 667 00:40:47,660 --> 00:40:52,340 It has the function prototype that we expect middleware to be. 668 00:40:52,340 --> 00:40:53,710 It takes a store. 669 00:40:53,710 --> 00:41:00,580 And if we wanted to, we can use object spread in order to extract the getState 670 00:41:00,580 --> 00:41:02,270 and dispatch methods there. 671 00:41:02,270 --> 00:41:05,900 But instead, we'll call it store and we'll pass store.dispatch. 672 00:41:05,900 --> 00:41:10,940 We then return a function that expects whatever's next. 673 00:41:10,940 --> 00:41:13,400 And then we return a function that expects an action. 674 00:41:13,400 --> 00:41:16,880 And then we get to mess with the action if we want. 675 00:41:16,880 --> 00:41:20,150 And so we've gone ahead and implemented some middleware that takes 676 00:41:20,150 --> 00:41:22,520 care of asynchronous actions for us. 677 00:41:22,520 --> 00:41:25,310 And, actually, this is not a problem that we're the first ones 678 00:41:25,310 --> 00:41:28,050 to run into, believe it or not. 679 00:41:28,050 --> 00:41:31,490 There's actually a library called Redux Thunk 680 00:41:31,490 --> 00:41:38,040 by a man named Dan Abramov, who actually implemented a very similar thing. 681 00:41:38,040 --> 00:41:40,100 And if you look at the README, he explains 682 00:41:40,100 --> 00:41:44,180 that a thunk is a function that wraps an expression to delay its evaluation. 683 00:41:44,180 --> 00:41:48,200 That's just why he happened to name the package Redux Thunk. 684 00:41:48,200 --> 00:41:54,860 And if we open the page, we can see a few things. 685 00:41:54,860 --> 00:41:59,606 We see that this "allows us to write action creators that return 686 00:41:59,606 --> 00:42:00,980 a function instead of an action." 687 00:42:00,980 --> 00:42:04,336 So very similar to our particular implementation. 688 00:42:04,336 --> 00:42:08,030 "The thunk can be used to delay the dispatch of an action, 689 00:42:08,030 --> 00:42:11,140 or to dispatch only if a certain condition is met." 690 00:42:11,140 --> 00:42:15,140 Or, in other words, "an action creator that returns a function 691 00:42:15,140 --> 00:42:16,865 to perform an asynchronous dispatch." 692 00:42:16,865 --> 00:42:21,530 And so this library solves the same problem that we just solved. 693 00:42:21,530 --> 00:42:25,950 And let's actually look at how he was able to solve it. 694 00:42:25,950 --> 00:42:29,480 So this is the first time that we're looking at an open source library 695 00:42:29,480 --> 00:42:33,140 and actually starting to open up how it works under the hood. 696 00:42:33,140 --> 00:42:36,410 And so let's actually look in the source folder 697 00:42:36,410 --> 00:42:40,040 and look at his particular implementation. 698 00:42:40,040 --> 00:42:41,340 And what do we see? 699 00:42:41,340 --> 00:42:43,790 We see a function called createThunkMiddleware. 700 00:42:43,790 --> 00:42:47,500 It takes an extra argument, something that we didn't really worry about. 701 00:42:47,500 --> 00:42:49,970 But then, we see this very familiar function prototype. 702 00:42:49,970 --> 00:42:56,990 That prototype happens to exactly match the prototype 703 00:42:56,990 --> 00:43:00,650 that we're looking for for middleware. 704 00:43:00,650 --> 00:43:02,150 And then what does it do? 705 00:43:02,150 --> 00:43:05,580 It checks to see if the type of the action is a function. 706 00:43:05,580 --> 00:43:09,560 So, in other words, if the action is a thunk, or a function that's 707 00:43:09,560 --> 00:43:16,520 delaying its evaluation, or if it's just a normal action. 708 00:43:16,520 --> 00:43:20,110 And if it is a thunk, what it does is it invokes the action 709 00:43:20,110 --> 00:43:24,290 and passes it dispatch, getState, and maybe an extra argument if you so 710 00:43:24,290 --> 00:43:26,074 chose. 711 00:43:26,074 --> 00:43:27,990 And then, if it's not, it returns next action. 712 00:43:27,990 --> 00:43:30,470 So something very, very familiar to us. 713 00:43:30,470 --> 00:43:33,110 And then it ends up doing some additional configuration, where 714 00:43:33,110 --> 00:43:35,450 it creates this thing called thunk, which 715 00:43:35,450 --> 00:43:38,990 is invoking this createThunkMiddleware with no extra argument. 716 00:43:38,990 --> 00:43:41,210 Which ends up basically just moving this, 717 00:43:41,210 --> 00:43:44,210 so it gets even closer to what we implemented. 718 00:43:44,210 --> 00:43:47,130 And then it exports its default export as this thunk. 719 00:43:47,130 --> 00:43:53,570 So this man Dan Abramov wrote a library that handles asynchronous actions, 720 00:43:53,570 --> 00:43:56,240 and his implementation was almost exactly 721 00:43:56,240 --> 00:43:57,890 the way that we implemented our own. 722 00:43:57,890 --> 00:44:00,990 723 00:44:00,990 --> 00:44:03,200 So let's actually use this. 724 00:44:03,200 --> 00:44:07,850 725 00:44:07,850 --> 00:44:10,340 So rather than using our own thunk middleware, 726 00:44:10,340 --> 00:44:15,980 let's just go ahead and import his, so let's just 727 00:44:15,980 --> 00:44:19,902 do import thunk from redux-thunk. 728 00:44:19,902 --> 00:44:23,030 729 00:44:23,030 --> 00:44:25,580 Delete our particular implementation. 730 00:44:25,580 --> 00:44:28,499 Actually, let me save this for reference. 731 00:44:28,499 --> 00:44:34,490 732 00:44:34,490 --> 00:44:36,560 And now we're basically where we were before. 733 00:44:36,560 --> 00:44:38,934 But rather than having to re-implement our own middleware 734 00:44:38,934 --> 00:44:42,310 every single time, now we can rely on an external library, and a very popular 735 00:44:42,310 --> 00:44:44,830 one at that. 736 00:44:44,830 --> 00:44:49,520 Before we run this, we're of course going to want to install it. 737 00:44:49,520 --> 00:44:55,530 738 00:44:55,530 --> 00:44:58,740 And as soon as it's installed, now our app can handle asynchronous actions. 739 00:44:58,740 --> 00:45:03,180 And let's go ahead and start using those asynchronous actions. 740 00:45:03,180 --> 00:45:07,650 And so let's go ahead and actually re-add our login 741 00:45:07,650 --> 00:45:11,380 screen to our application, which we removed last week. 742 00:45:11,380 --> 00:45:14,820 So the way that we're going to do that is let's just reopen our app. 743 00:45:14,820 --> 00:45:20,370 And rather than rendering our main tabs by default, 744 00:45:20,370 --> 00:45:22,350 let's render our actual app navigator. 745 00:45:22,350 --> 00:45:35,530 746 00:45:35,530 --> 00:45:38,890 So, once in a while, your dependencies might get out 747 00:45:38,890 --> 00:45:41,100 of sync with what's running in the packager, 748 00:45:41,100 --> 00:45:45,010 and so you might have to reload your bundle to make sure that all 749 00:45:45,010 --> 00:45:47,022 of the dependencies get picked up. 750 00:45:47,022 --> 00:45:49,480 And so, as this builds, let's think about exactly how we're 751 00:45:49,480 --> 00:45:53,020 going to add that login screen back to our app. 752 00:45:53,020 --> 00:45:56,470 And so the way that we did this a few weeks ago is we 753 00:45:56,470 --> 00:46:01,070 had an API where we defined a function called login. 754 00:46:01,070 --> 00:46:02,560 That is still around. 755 00:46:02,560 --> 00:46:10,160 756 00:46:10,160 --> 00:46:15,440 So in our api.js file, we have this login function that we defined, 757 00:46:15,440 --> 00:46:17,750 that takes a username and password. 758 00:46:17,750 --> 00:46:21,600 It will fetch to our particular-- 759 00:46:21,600 --> 00:46:24,390 the port where our authentication server is running. 760 00:46:24,390 --> 00:46:27,090 It will send the username and password. 761 00:46:27,090 --> 00:46:29,480 And if it's OK, then it's going to say, hey, it worked. 762 00:46:29,480 --> 00:46:31,730 And if not, it's going to throw an error. 763 00:46:31,730 --> 00:46:35,930 And we're going to have to use that login function in order 764 00:46:35,930 --> 00:46:37,700 to log in our user. 765 00:46:37,700 --> 00:46:50,950 And the way that we did that a few weeks ago is in our screens, the login page. 766 00:46:50,950 --> 00:46:54,790 We had a function called login, which would try 767 00:46:54,790 --> 00:46:58,130 to handle all of the login logic here. 768 00:46:58,130 --> 00:47:03,460 So it would await the result of sending that asynchronous login. 769 00:47:03,460 --> 00:47:10,660 If it's successful, then it will navigate us over to our main page. 770 00:47:10,660 --> 00:47:14,890 And if it's unsuccessful, then it will set the state here 771 00:47:14,890 --> 00:47:18,750 to the message, which then shows up in our UI. 772 00:47:18,750 --> 00:47:20,830 And so we have some text here, that is the error. 773 00:47:20,830 --> 00:47:23,020 And if there's an error, it will go ahead and show. 774 00:47:23,020 --> 00:47:24,160 So let's check on this. 775 00:47:24,160 --> 00:47:26,890 And so this is exactly what we saw. 776 00:47:26,890 --> 00:47:32,419 So if we try to log in and there's no password, then it will let us know, 777 00:47:32,419 --> 00:47:34,210 hey, you're missing a username or password. 778 00:47:34,210 --> 00:47:38,770 If we have incorrect username and password, the user might not exist. 779 00:47:38,770 --> 00:47:41,390 If we use a user that we know exists with the wrong password, 780 00:47:41,390 --> 00:47:43,610 it will let us know that it's the wrong password. 781 00:47:43,610 --> 00:47:48,400 And, finally, the correct username with its correct password, 782 00:47:48,400 --> 00:47:50,310 then it will log us in. 783 00:47:50,310 --> 00:47:56,120 And so that, again, is all done in this class called LoginScreen. 784 00:47:56,120 --> 00:48:00,687 So let's now start to use Redux here, rather than using the logic directly 785 00:48:00,687 --> 00:48:01,270 on this class. 786 00:48:01,270 --> 00:48:03,760 And so what have we done in the past when 787 00:48:03,760 --> 00:48:11,620 we want to move logic out of a component and into our Redux setup? 788 00:48:11,620 --> 00:48:15,700 789 00:48:15,700 --> 00:48:26,880 Well, first we need to add maybe some new action types. 790 00:48:26,880 --> 00:48:29,790 So let's go into actions.js and then add a couple of action types. 791 00:48:29,790 --> 00:48:33,290 So right now we have UPDATE_USER and UPDATE_CONTACT. 792 00:48:33,290 --> 00:48:41,860 Let's also add an action type for sending out a login request. 793 00:48:41,860 --> 00:48:44,270 So let's do LOG_IN_SENT. 794 00:48:44,270 --> 00:48:49,250 795 00:48:49,250 --> 00:48:54,800 Let's have an action type for when it succeeds, LOG_IN, 796 00:48:54,800 --> 00:48:56,790 and let's call it FULFILLED. 797 00:48:56,790 --> 00:49:02,700 798 00:49:02,700 --> 00:49:08,130 And, lastly, let's have an action type for if login 799 00:49:08,130 --> 00:49:09,530 did not succeed or got rejected. 800 00:49:09,530 --> 00:49:17,320 801 00:49:17,320 --> 00:49:17,820 Cool. 802 00:49:17,820 --> 00:49:20,490 So now we have all of the action types that we need. 803 00:49:20,490 --> 00:49:23,040 We are, however, missing the action creators. 804 00:49:23,040 --> 00:49:27,330 So let's go and actually steal the action creator that we wrote 805 00:49:27,330 --> 00:49:31,869 in our simpleRedux implementation. 806 00:49:31,869 --> 00:49:40,830 807 00:49:40,830 --> 00:49:43,370 So let me just cut and paste that one. 808 00:49:43,370 --> 00:49:56,840 809 00:49:56,840 --> 00:50:00,644 So now we have an asynchronous action creator called logInUser 810 00:50:00,644 --> 00:50:02,185 that expects a username and password. 811 00:50:02,185 --> 00:50:05,590 812 00:50:05,590 --> 00:50:08,710 It then returns a function that expects a dispatch, 813 00:50:08,710 --> 00:50:12,760 and then dispatches something of type-- 814 00:50:12,760 --> 00:50:17,290 we renamed it to LOG_IN_SENT here. 815 00:50:17,290 --> 00:50:19,810 It then tries to actually log in. 816 00:50:19,810 --> 00:50:24,074 If it's successful, then it will dispatch the LOG_IN_SUCCESS. 817 00:50:24,074 --> 00:50:27,210 818 00:50:27,210 --> 00:50:29,560 We call it LOG_IN_FULFILLED. 819 00:50:29,560 --> 00:50:33,070 And, lastly, if it is unsuccessful or throws an error, 820 00:50:33,070 --> 00:50:39,090 we dispatch something called LOG_IN_REJECTED. 821 00:50:39,090 --> 00:50:39,590 Great. 822 00:50:39,590 --> 00:50:45,175 So now we have an asynchronous action creator, that we first need to export. 823 00:50:45,175 --> 00:50:47,300 And now, how are we going to go ahead and use that? 824 00:50:47,300 --> 00:50:52,050 How do we import our action creators, bind them to dispatch, 825 00:50:52,050 --> 00:50:55,590 and have them available for us to use in our application? 826 00:50:55,590 --> 00:50:59,430 Well, that's exactly what React Redux does for us, 827 00:50:59,430 --> 00:51:01,860 and that connect higher-order component. 828 00:51:01,860 --> 00:51:08,100 And so in screens/LoginScreen, let's go ahead 829 00:51:08,100 --> 00:51:22,170 and import connect from react-redux, and also import our action. 830 00:51:22,170 --> 00:51:32,590 So import logInUser from the Redux actions that we defined. 831 00:51:32,590 --> 00:51:36,490 And now that all of that login logic is in our action creator, 832 00:51:36,490 --> 00:51:42,380 we can now not use the login from API within this class. 833 00:51:42,380 --> 00:51:48,950 So now, how do we get that logInUser to work correctly in this class? 834 00:51:48,950 --> 00:51:51,520 835 00:51:51,520 --> 00:51:55,180 Do we do something like logInUser and then 836 00:51:55,180 --> 00:52:01,210 pass this.state.username and this.state.password? 837 00:52:01,210 --> 00:52:05,860 838 00:52:05,860 --> 00:52:08,440 No, we don't, because what does that actually do? 839 00:52:08,440 --> 00:52:09,670 What does logInUser do? 840 00:52:09,670 --> 00:52:13,360 841 00:52:13,360 --> 00:52:18,590 logInUser, all it does is it returns a new function that expects a dispatch 842 00:52:18,590 --> 00:52:20,450 and will do a bunch of things. 843 00:52:20,450 --> 00:52:23,450 And so, by just invoking that straight here, 844 00:52:23,450 --> 00:52:26,570 all this does is return a function, and we never actually do anything 845 00:52:26,570 --> 00:52:27,690 with that function. 846 00:52:27,690 --> 00:52:30,380 So what do we actually have to do with that function? 847 00:52:30,380 --> 00:52:34,700 Well, we need to store.dispatch it so that Redux knows, 848 00:52:34,700 --> 00:52:39,210 and more specifically, our middleware knows exactly how to handle that. 849 00:52:39,210 --> 00:52:44,710 And so how do we, then, go ahead and do store.dispatch this function here? 850 00:52:44,710 --> 00:52:47,150 Well, we could import store and do store.dispatch, 851 00:52:47,150 --> 00:52:50,430 or we could just use connect. 852 00:52:50,430 --> 00:52:55,295 And so, rather than default exporting LoginScreen here, what we do is we 853 00:52:55,295 --> 00:53:04,860 default export the LoginScreen, but we actually 854 00:53:04,860 --> 00:53:09,750 pass that in to our higher-order component called connect. 855 00:53:09,750 --> 00:53:13,150 856 00:53:13,150 --> 00:53:15,980 And what are we going to pass to connect for parameters? 857 00:53:15,980 --> 00:53:19,880 Well, for now, we're not going to care about anything in our Redux store, 858 00:53:19,880 --> 00:53:26,540 but we want to bind the logInUser to the dispatch. 859 00:53:26,540 --> 00:53:29,090 And so now this is going to be passed down as a prop, 860 00:53:29,090 --> 00:53:32,840 so we could do this.props.logInUser. 861 00:53:32,840 --> 00:53:35,960 And that will go ahead and do exactly what we want. 862 00:53:35,960 --> 00:53:39,902 And so what is missing? 863 00:53:39,902 --> 00:53:41,360 Let's go ahead and try to run this. 864 00:53:41,360 --> 00:53:44,190 865 00:53:44,190 --> 00:53:45,975 Username, password. 866 00:53:45,975 --> 00:53:48,690 867 00:53:48,690 --> 00:53:52,030 So that logged us in perfectly fine. 868 00:53:52,030 --> 00:53:58,040 But what happens if we put something that should actually be error? 869 00:53:58,040 --> 00:54:00,840 870 00:54:00,840 --> 00:54:02,415 It worked for some strange reason. 871 00:54:02,415 --> 00:54:05,820 872 00:54:05,820 --> 00:54:11,020 Oh, because we still have the old call on there. 873 00:54:11,020 --> 00:54:15,780 Oh, I've got default export when I meant to write export default that-- 874 00:54:15,780 --> 00:54:18,470 875 00:54:18,470 --> 00:54:23,105 And we can now run it, try using the correct username and password. 876 00:54:23,105 --> 00:54:25,670 877 00:54:25,670 --> 00:54:27,830 And we can't find the variable login, which 878 00:54:27,830 --> 00:54:30,720 would make sense because we did not import it into our Redux actions. 879 00:54:30,720 --> 00:54:36,710 880 00:54:36,710 --> 00:54:44,820 So we need to import that login function that we created in our API 881 00:54:44,820 --> 00:54:48,480 since we go ahead and use it down here. 882 00:54:48,480 --> 00:54:50,720 So now let's go ahead and see if this works. 883 00:54:50,720 --> 00:54:56,470 884 00:54:56,470 --> 00:54:59,910 And it goes ahead and logs us in perfectly fine. 885 00:54:59,910 --> 00:55:04,650 But now let's see what happens if we put something invalid. 886 00:55:04,650 --> 00:55:07,110 It also logs us in perfectly fine. 887 00:55:07,110 --> 00:55:12,840 And so let's take a short break, and then after the break fix everything up 888 00:55:12,840 --> 00:55:16,410 and use Redux in our Redux, in our contacts app. 889 00:55:16,410 --> 00:55:17,550 Hello, and welcome back. 890 00:55:17,550 --> 00:55:21,090 So, before the break, what we were doing is we were working on our contacts app 891 00:55:21,090 --> 00:55:24,900 and re-adding our login functionality by using Redux. 892 00:55:24,900 --> 00:55:30,870 And, so far, what we've done is we've added to Redux our Redux actions. 893 00:55:30,870 --> 00:55:32,700 We've added a few action types, which we're 894 00:55:32,700 --> 00:55:36,480 going to need in order to know exactly what we're doing 895 00:55:36,480 --> 00:55:39,150 in terms of sending off the login. 896 00:55:39,150 --> 00:55:42,870 So we have an action type for when we send the initial request. 897 00:55:42,870 --> 00:55:45,320 We have one for when it comes back with a success. 898 00:55:45,320 --> 00:55:47,610 And we have an action type where it comes back 899 00:55:47,610 --> 00:55:52,120 with an error, where maybe we used the wrong password or something like that. 900 00:55:52,120 --> 00:55:54,630 We also wrote an action creator, an asynchronous one, 901 00:55:54,630 --> 00:55:58,410 which, rather than returning an object, we return a thunk. 902 00:55:58,410 --> 00:56:02,610 We return a function which expects as an argument a way 903 00:56:02,610 --> 00:56:05,220 so that it knows how to dispatch its own actions. 904 00:56:05,220 --> 00:56:10,710 And so this async action creator here takes a username and password, 905 00:56:10,710 --> 00:56:13,530 returns a function that expects dispatch, or it expects 906 00:56:13,530 --> 00:56:17,040 to know a way to dispatch its own actions, 907 00:56:17,040 --> 00:56:19,390 and then immediately dispatches an action that says, 908 00:56:19,390 --> 00:56:21,630 hey, I'm sending off a request. 909 00:56:21,630 --> 00:56:23,460 It goes ahead and sends off the request. 910 00:56:23,460 --> 00:56:27,240 If it comes back without an error, it dispatches an action 911 00:56:27,240 --> 00:56:28,740 that says, hey, we're all good. 912 00:56:28,740 --> 00:56:30,150 It came back. 913 00:56:30,150 --> 00:56:35,100 And if it comes back with an error, then it says, oh, sorry. 914 00:56:35,100 --> 00:56:37,660 This got rejected. 915 00:56:37,660 --> 00:56:40,740 What we haven't yet done is gone into a reducer 916 00:56:40,740 --> 00:56:44,670 and listened for those action types and told our reducer what to do 917 00:56:44,670 --> 00:56:48,480 or how to change the state when those different types come back. 918 00:56:48,480 --> 00:56:51,000 And since we haven't finished implementing that, 919 00:56:51,000 --> 00:56:54,720 basically what happens is our application 920 00:56:54,720 --> 00:56:56,970 lets us in without any authentication. 921 00:56:56,970 --> 00:57:00,210 And so let's go ahead and build out the rest of the functionality such 922 00:57:00,210 --> 00:57:04,574 that the login screen works exactly as expected. 923 00:57:04,574 --> 00:57:06,240 So what are we going to need to do here? 924 00:57:06,240 --> 00:57:11,430 Well, first we're going to need to open up a reducer 925 00:57:11,430 --> 00:57:15,650 and change the state of our app when these different actions appear. 926 00:57:15,650 --> 00:57:18,280 And so let's go ahead and import a few different actions. 927 00:57:18,280 --> 00:57:21,460 So we created LOG_IN_SENT. 928 00:57:21,460 --> 00:57:24,210 929 00:57:24,210 --> 00:57:26,490 We created LOG_IN_FULFILLED. 930 00:57:26,490 --> 00:57:30,270 931 00:57:30,270 --> 00:57:32,655 And we created LOG_IN_REJECTED. 932 00:57:32,655 --> 00:57:36,700 933 00:57:36,700 --> 00:57:38,200 And so now, what are we going to do? 934 00:57:38,200 --> 00:57:42,330 Well, we should look in our userReducer and handle those cases. 935 00:57:42,330 --> 00:57:49,470 So if this is LOG_IN_FULFILLED, what should we do? 936 00:57:49,470 --> 00:57:57,520 Well, maybe it's going to be fulfilled with a login token, or something 937 00:57:57,520 --> 00:57:58,020 like that. 938 00:57:58,020 --> 00:58:00,690 So let's just assume that a token is coming back in the payload 939 00:58:00,690 --> 00:58:02,330 and set that here. 940 00:58:02,330 --> 00:58:05,774 941 00:58:05,774 --> 00:58:07,940 We'll handle the case where the login gets rejected. 942 00:58:07,940 --> 00:58:13,850 943 00:58:13,850 --> 00:58:20,060 And let's create something like login error, where the error 944 00:58:20,060 --> 00:58:21,700 message is whatever the payload is. 945 00:58:21,700 --> 00:58:25,480 946 00:58:25,480 --> 00:58:27,790 And so, because we're using Flux standard actions, 947 00:58:27,790 --> 00:58:31,930 every single action is going to have a type and some payload, 948 00:58:31,930 --> 00:58:34,951 where the payload is whatever is important for that particular action 949 00:58:34,951 --> 00:58:35,450 type. 950 00:58:35,450 --> 00:58:39,190 And so, in the case of UPDATE_CONTACT, it's going to be the previous contact. 951 00:58:39,190 --> 00:58:43,270 In the case of LOG_IN_FULFILLED, it's going to be whatever token we get back, 952 00:58:43,270 --> 00:58:44,641 authentication token. 953 00:58:44,641 --> 00:58:46,390 And if the login gets rejected, it's going 954 00:58:46,390 --> 00:58:48,684 to be, presumably, some sort of error message, 955 00:58:48,684 --> 00:58:50,850 where we can set the login error to be that payload. 956 00:58:50,850 --> 00:58:54,950 957 00:58:54,950 --> 00:58:57,620 So let's actually make that happen. 958 00:58:57,620 --> 00:59:02,920 So, in our actions, currently when we receive an error back from this, 959 00:59:02,920 --> 00:59:04,370 we're not passing in any payload. 960 00:59:04,370 --> 00:59:08,095 So let's actually pass in the payload, where the payload is actually 961 00:59:08,095 --> 00:59:08,845 the error message. 962 00:59:08,845 --> 00:59:11,870 963 00:59:11,870 --> 00:59:15,700 And so how about with LOG_IN_FULFILLED? 964 00:59:15,700 --> 00:59:18,800 So, currently, what we're doing in our API function is, 965 00:59:18,800 --> 00:59:22,790 if it comes back as a success, we're just returning true. 966 00:59:22,790 --> 00:59:25,740 But let's actually go fix that so it returns a token. 967 00:59:25,740 --> 00:59:33,950 And so here, let's expect the payload to come back as a token, 968 00:59:33,950 --> 00:59:36,080 and expect that login, if it works, it's going 969 00:59:36,080 --> 00:59:39,110 to fulfill or return with a token. 970 00:59:39,110 --> 00:59:42,470 971 00:59:42,470 --> 00:59:46,040 So let's just go quickly change our API function. 972 00:59:46,040 --> 00:59:50,300 So, right now, login, it will try to log in. 973 00:59:50,300 --> 00:59:52,770 If it worked, we're returning true. 974 00:59:52,770 --> 00:59:54,700 But let's actually do something instead. 975 00:59:54,700 --> 00:59:57,010 If it works, let's actually take-- 976 00:59:57,010 --> 01:00:00,790 let's extract from the response a token. 977 01:00:00,790 --> 01:00:03,340 The reason that we know a token is coming back 978 01:00:03,340 --> 01:00:09,280 is because, in our authserver, we actually changed the code-- 979 01:00:09,280 --> 01:00:13,350 or I changed the code here. 980 01:00:13,350 --> 01:00:15,580 So rather than just returning an OK, let's 981 01:00:15,580 --> 01:00:19,330 actually return some JSON that says-- 982 01:00:19,330 --> 01:00:22,345 the token is thisIsARealToken. 983 01:00:22,345 --> 01:00:25,840 984 01:00:25,840 --> 01:00:28,330 So, again, you don't need to understand what's happening 985 01:00:28,330 --> 01:00:33,960 in this server code, but a quick-- 986 01:00:33,960 --> 01:00:39,490 if there is no username or password, return this error that says, 987 01:00:39,490 --> 01:00:42,160 we're missing a username or password. 988 01:00:42,160 --> 01:00:44,650 If the user doesn't exist, say, the user doesn't exist. 989 01:00:44,650 --> 01:00:48,290 If the password is incorrect, say, incorrect password. 990 01:00:48,290 --> 01:00:49,630 Otherwise, we're good. 991 01:00:49,630 --> 01:00:52,300 This is the correct username and password combo, 992 01:00:52,300 --> 01:00:55,210 so return just a fake token. 993 01:00:55,210 --> 01:00:58,582 And the fake token is the string thisIsARealToken. 994 01:00:58,582 --> 01:01:01,360 995 01:01:01,360 --> 01:01:06,250 So now, in our API file, we know that if the response is OK, 996 01:01:06,250 --> 01:01:11,320 we should be looking for a JSON response that has a token in it. 997 01:01:11,320 --> 01:01:18,390 So how, again, do we extract JSON from a response type? 998 01:01:18,390 --> 01:01:20,500 So, if you remember back to the lecture on data, 999 01:01:20,500 --> 01:01:30,790 the way that we do that is we do const json is await whatever the response is 1000 01:01:30,790 --> 01:01:31,460 .json. 1001 01:01:31,460 --> 01:01:38,229 And that .json method says, hey, extract the JSON from this response and return 1002 01:01:38,229 --> 01:01:39,520 it to me whenever it gets back. 1003 01:01:39,520 --> 01:01:45,360 And it returns a promise, and we'll go ahead and await that promise. 1004 01:01:45,360 --> 01:01:47,180 And now we can just return json.token. 1005 01:01:47,180 --> 01:01:50,290 1006 01:01:50,290 --> 01:01:55,720 Or, if you want to use the shorthand, we can use this object destructuring 1007 01:01:55,720 --> 01:01:58,270 to create a new constant called token. 1008 01:01:58,270 --> 01:02:01,960 The value of that is whatever the value is of the key 1009 01:02:01,960 --> 01:02:04,784 called token in whatever this returns. 1010 01:02:04,784 --> 01:02:06,200 And then we can return token here. 1011 01:02:06,200 --> 01:02:09,250 1012 01:02:09,250 --> 01:02:12,220 And then, if the response is not OK, we're 1013 01:02:12,220 --> 01:02:16,000 going to grab the error message from whatever text comes back. 1014 01:02:16,000 --> 01:02:18,280 And, again, we have to await the response of that 1015 01:02:18,280 --> 01:02:20,210 because it returns a promise. 1016 01:02:20,210 --> 01:02:22,900 And then we're going to throw an error with that error message. 1017 01:02:22,900 --> 01:02:29,744 And we go ahead and extract that error message back out in our Redux action. 1018 01:02:29,744 --> 01:02:32,837 So, down here, we try to log in. 1019 01:02:32,837 --> 01:02:35,170 If we're successful, we're just going to return a token, 1020 01:02:35,170 --> 01:02:39,230 and we can dispatch LOG_IN_FULFILLED with that token. 1021 01:02:39,230 --> 01:02:41,200 If there's an error in that function, then we 1022 01:02:41,200 --> 01:02:46,720 can extract the error message from that and dispatch 1023 01:02:46,720 --> 01:02:50,510 a action of the type LOG_IN_REJECTED. 1024 01:02:50,510 --> 01:02:55,000 And if you're not very familiar with this promise syntax, 1025 01:02:55,000 --> 01:03:02,670 we can also refactor it to async await, so async dispatch. 1026 01:03:02,670 --> 01:03:08,940 1027 01:03:08,940 --> 01:03:17,930 We can await the result of the login and dispatch an action, assuming 1028 01:03:17,930 --> 01:03:20,040 it's successful with that payload. 1029 01:03:20,040 --> 01:03:23,250 And then, if it's errors, we want to catch that error 1030 01:03:23,250 --> 01:03:26,700 and dispatch that error action. 1031 01:03:26,700 --> 01:03:27,630 So we can try this. 1032 01:03:27,630 --> 01:03:32,850 1033 01:03:32,850 --> 01:03:35,300 We can catch the error. 1034 01:03:35,300 --> 01:03:40,770 1035 01:03:40,770 --> 01:03:48,110 And if there is an error, then we can dispatch this particular action type. 1036 01:03:48,110 --> 01:03:51,840 1037 01:03:51,840 --> 01:03:52,340 Great. 1038 01:03:52,340 --> 01:03:55,840 So now we fixed everything in our API. 1039 01:03:55,840 --> 01:03:58,730 We fixed the things in our actions, and we 1040 01:03:58,730 --> 01:04:04,260 went ahead and implemented our reducer to check for those particular values. 1041 01:04:04,260 --> 01:04:09,650 So to just review that really quick, if we 1042 01:04:09,650 --> 01:04:13,410 receive an action called LOG_IN_FULFILLED, 1043 01:04:13,410 --> 01:04:22,640 then we know from our actions that the payload is going to be the token. 1044 01:04:22,640 --> 01:04:26,540 So we can go ahead and merge the previous state 1045 01:04:26,540 --> 01:04:32,840 with a new state that has a key called token, where the value is that payload. 1046 01:04:32,840 --> 01:04:35,930 If we have an error because it's rejected, 1047 01:04:35,930 --> 01:04:38,390 we can go ahead and dispatch this action, 1048 01:04:38,390 --> 01:04:41,960 with the payload as the error message, look for it here, 1049 01:04:41,960 --> 01:04:48,150 and return a new state that has a login error where the value is that payload. 1050 01:04:48,150 --> 01:04:50,210 So that's all good on the Redux side. 1051 01:04:50,210 --> 01:04:56,270 Now let's actually add it to our particular class. 1052 01:04:56,270 --> 01:05:03,450 And so let's look at our LoginScreen and review what's happening here. 1053 01:05:03,450 --> 01:05:06,680 So, currently, when the asynchronous function is dispatched, 1054 01:05:06,680 --> 01:05:12,280 what we do is we invoke the action where we do this.props.logInUser. 1055 01:05:12,280 --> 01:05:14,540 We pass the username and password. 1056 01:05:14,540 --> 01:05:17,840 That dispatches all of those actions that we defined in Redux. 1057 01:05:17,840 --> 01:05:19,956 It stores it up in the state. 1058 01:05:19,956 --> 01:05:21,830 And then what we do is we just log in anyway. 1059 01:05:21,830 --> 01:05:23,579 So that's probably not what we want to do. 1060 01:05:23,579 --> 01:05:27,400 Instead, what we should do is look at those values in the state, 1061 01:05:27,400 --> 01:05:31,340 and when they change to things that we want to react to, 1062 01:05:31,340 --> 01:05:33,980 we should do that in this particular class. 1063 01:05:33,980 --> 01:05:40,220 And so how again do we listen to those values that change in the state? 1064 01:05:40,220 --> 01:05:44,810 So we're going to have to use that higher-order component called connect. 1065 01:05:44,810 --> 01:05:46,820 So, down here, what we're doing with connect 1066 01:05:46,820 --> 01:05:49,580 is we're passing the first argument as null. 1067 01:05:49,580 --> 01:05:51,620 And if you recall what that first argument is, 1068 01:05:51,620 --> 01:05:56,570 it's a function that takes whatever our Redux store is and maps 1069 01:05:56,570 --> 01:05:58,880 particular values in the store to be props that 1070 01:05:58,880 --> 01:06:01,100 are passed into this particular class. 1071 01:06:01,100 --> 01:06:04,140 So let's now define that function. 1072 01:06:04,140 --> 01:06:09,740 So let's just call it mapStateToProps, just because that's convention. 1073 01:06:09,740 --> 01:06:14,450 And it's going to take the state of our application and return an object. 1074 01:06:14,450 --> 01:06:18,680 And the object key-value pairs refer to the props that are passed. 1075 01:06:18,680 --> 01:06:25,010 And so we're going to have a prop called error, or err for short. 1076 01:06:25,010 --> 01:06:26,320 And what's that going to be? 1077 01:06:26,320 --> 01:06:30,710 Well, it's going to be the state.user, because, if you recall, 1078 01:06:30,710 --> 01:06:33,822 that userReducer is only the user key of that state. 1079 01:06:33,822 --> 01:06:35,030 And then what did we call it? 1080 01:06:35,030 --> 01:06:36,571 We called it something like loginErr. 1081 01:06:36,571 --> 01:06:38,562 1082 01:06:38,562 --> 01:06:40,020 What else are we going to look for? 1083 01:06:40,020 --> 01:06:43,040 Well, presumably we're going to leave the screen 1084 01:06:43,040 --> 01:06:45,860 when we have a token in our state. 1085 01:06:45,860 --> 01:06:49,260 So let's look for the token, and that value is going to be state.user.token. 1086 01:06:49,260 --> 01:06:52,111 1087 01:06:52,111 --> 01:06:52,610 Cool. 1088 01:06:52,610 --> 01:06:55,894 So now we are now effectively listening for the values 1089 01:06:55,894 --> 01:06:57,060 that we put up in the state. 1090 01:06:57,060 --> 01:07:02,960 So let's just ensure that those are the values that we actually 1091 01:07:02,960 --> 01:07:04,460 do, so let's look at our reducer. 1092 01:07:04,460 --> 01:07:10,790 1093 01:07:10,790 --> 01:07:13,130 So we see, for our userReducer, we're adding 1094 01:07:13,130 --> 01:07:18,840 this thing called a token, which we're looking for here, state.user.token. 1095 01:07:18,840 --> 01:07:21,840 We're adding this thing called a loginErr, which we're looking for here, 1096 01:07:21,840 --> 01:07:24,020 state.user.loginErr. 1097 01:07:24,020 --> 01:07:27,830 So, if everything goes correctly, what should happen 1098 01:07:27,830 --> 01:07:29,630 is we should expect a couple of new props. 1099 01:07:29,630 --> 01:07:34,300 And so let's just define those here. 1100 01:07:34,300 --> 01:07:38,560 1101 01:07:38,560 --> 01:07:43,840 So we're expecting something like an error, which is going to be a string. 1102 01:07:43,840 --> 01:07:47,140 We're expecting a token, which could be a string. 1103 01:07:47,140 --> 01:07:51,290 1104 01:07:51,290 --> 01:07:56,030 And, lastly, we're expecting a function called logInUser here. 1105 01:07:56,030 --> 01:07:59,330 1106 01:07:59,330 --> 01:08:00,710 That's a function. 1107 01:08:00,710 --> 01:08:03,690 1108 01:08:03,690 --> 01:08:06,340 And we're going to have to import PropTypes. 1109 01:08:06,340 --> 01:08:09,454 1110 01:08:09,454 --> 01:08:11,370 And so I'm just writing that for documentation 1111 01:08:11,370 --> 01:08:12,703 so we know what we're expecting. 1112 01:08:12,703 --> 01:08:19,140 So we get error and token because we're looking for those in our state. 1113 01:08:19,140 --> 01:08:23,569 And we get logInUser because we're binding that in our connect function. 1114 01:08:23,569 --> 01:08:26,810 So the last thing we need to do is, rather than passing null here, 1115 01:08:26,810 --> 01:08:28,550 pass our mapStateToProps function. 1116 01:08:28,550 --> 01:08:32,010 1117 01:08:32,010 --> 01:08:32,640 Great. 1118 01:08:32,640 --> 01:08:37,740 So now, when we send off this function, we probably 1119 01:08:37,740 --> 01:08:42,026 don't want to immediately navigate away, so let's delete that. 1120 01:08:42,026 --> 01:08:43,859 We no longer have to catch the error message 1121 01:08:43,859 --> 01:08:45,234 because that's now done in Redux. 1122 01:08:45,234 --> 01:08:46,770 So we can delete all of those. 1123 01:08:46,770 --> 01:08:49,170 We don't have to try because it's just going to work. 1124 01:08:49,170 --> 01:08:50,753 And so now we have this for our login. 1125 01:08:50,753 --> 01:08:53,439 1126 01:08:53,439 --> 01:08:54,660 So now what's happening? 1127 01:08:54,660 --> 01:08:58,890 If we try running this, we get no feedback. 1128 01:08:58,890 --> 01:09:02,050 We are not logged in, nor are we seeing any error. 1129 01:09:02,050 --> 01:09:02,979 Why is that? 1130 01:09:02,979 --> 01:09:05,819 Well, it's because we're not really looking for the error anywhere. 1131 01:09:05,819 --> 01:09:07,620 So, currently, the error that shows up is 1132 01:09:07,620 --> 01:09:09,630 this.state.err, which no longer exists. 1133 01:09:09,630 --> 01:09:15,890 Now it's this.props.err, which, again, we receive in our props. 1134 01:09:15,890 --> 01:09:20,760 And we receive it because this mapStateToProps function lets 1135 01:09:20,760 --> 01:09:25,979 us know that something's coming down called err, and the value of that 1136 01:09:25,979 --> 01:09:32,370 corresponds with the state.user.logInErr value in our Redux store. 1137 01:09:32,370 --> 01:09:35,609 So now, if we do this-- 1138 01:09:35,609 --> 01:09:38,220 I need to save the file. 1139 01:09:38,220 --> 01:09:41,090 Now, if we do this, we see that error come through. 1140 01:09:41,090 --> 01:09:43,426 1141 01:09:43,426 --> 01:09:44,550 Why is that coming through? 1142 01:09:44,550 --> 01:09:51,180 Let's just do a quick sanity check and see exactly the path of this request. 1143 01:09:51,180 --> 01:09:54,900 So first, what happens is we open this up. 1144 01:09:54,900 --> 01:09:58,800 We type in a text, our text inputs, everything. 1145 01:09:58,800 --> 01:10:02,440 And then we click that button that says, press to log in, which fires this 1146 01:10:02,440 --> 01:10:05,460 .login method. 1147 01:10:05,460 --> 01:10:06,210 What does that do? 1148 01:10:06,210 --> 01:10:09,480 Well, it invokes this.props.logInUser with the state, 1149 01:10:09,480 --> 01:10:11,430 with the username and the password. 1150 01:10:11,430 --> 01:10:13,890 And what is this.props.logInUser? 1151 01:10:13,890 --> 01:10:19,290 Well, it's our logInUser action creator, which 1152 01:10:19,290 --> 01:10:24,100 is actually bound to dispatch because connect does that for us. 1153 01:10:24,100 --> 01:10:26,730 And so, by invoking this.props.loginUser, 1154 01:10:26,730 --> 01:10:30,450 it's effectively doing our Redux store.dispatch 1155 01:10:30,450 --> 01:10:34,490 and then dispatching whatever the return value of logInUser is. 1156 01:10:34,490 --> 01:10:38,350 And so what is the return value there? 1157 01:10:38,350 --> 01:10:42,420 So we can just check our actions to see. 1158 01:10:42,420 --> 01:10:45,270 So we invoke logInUser with the username and password. 1159 01:10:45,270 --> 01:10:53,880 And it returns a function that expects another function called dispatch. 1160 01:10:53,880 --> 01:10:58,200 And that dispatch function is a way for our action creator 1161 01:10:58,200 --> 01:11:00,645 to know how to dispatch its own actions. 1162 01:11:00,645 --> 01:11:02,520 So the first thing that does is it dispatches 1163 01:11:02,520 --> 01:11:06,990 LOG_IN_SENT, which gets sent to our store, or reducer, I mean. 1164 01:11:06,990 --> 01:11:11,790 1165 01:11:11,790 --> 01:11:13,120 And what happens? 1166 01:11:13,120 --> 01:11:15,850 Well, we're not actually listening for that action type, 1167 01:11:15,850 --> 01:11:21,040 so it just returns the state as the default. Then it tries this. 1168 01:11:21,040 --> 01:11:24,610 It tries to await login username, password, which, in our API file, 1169 01:11:24,610 --> 01:11:25,900 does that request for us. 1170 01:11:25,900 --> 01:11:28,420 1171 01:11:28,420 --> 01:11:30,820 It awaits the value of that, and if it's successful, 1172 01:11:30,820 --> 01:11:34,720 then it's going to take a token. 1173 01:11:34,720 --> 01:11:38,050 But since we passed in the incorrect username and password, 1174 01:11:38,050 --> 01:11:39,610 it actually throws an error. 1175 01:11:39,610 --> 01:11:41,560 And the error gets caught here. 1176 01:11:41,560 --> 01:11:44,680 And now we dispatch an action called LOG_IN_REJECTED. 1177 01:11:44,680 --> 01:11:49,060 And the payload here is the error message for that particular error. 1178 01:11:49,060 --> 01:11:51,910 That gets dispatched and sent to a reducer. 1179 01:11:51,910 --> 01:11:54,160 It matches this here, LOG_IN_REJECTED. 1180 01:11:54,160 --> 01:11:58,420 It ends up merging the state, and adds into our user state 1181 01:11:58,420 --> 01:11:59,980 this thing called loginErr. 1182 01:11:59,980 --> 01:12:04,290 And the value of that is the payload, or the error message that comes back. 1183 01:12:04,290 --> 01:12:06,040 Then what happens with that error message? 1184 01:12:06,040 --> 01:12:16,480 Well, in our LoginScreen, we're actually listening for that error message. 1185 01:12:16,480 --> 01:12:20,190 We're saying here, every single time a Redux store changes, 1186 01:12:20,190 --> 01:12:23,380 update the props that are passed to this particular component 1187 01:12:23,380 --> 01:12:27,110 to be the values in the state that correspond with this object. 1188 01:12:27,110 --> 01:12:33,520 And so the error value here corresponds to the state.user.loginErr value that 1189 01:12:33,520 --> 01:12:35,290 just got updated in our Redux store. 1190 01:12:35,290 --> 01:12:40,550 So that gets passed down as a prop to this class. 1191 01:12:40,550 --> 01:12:43,590 That causes it to be re-rendered. 1192 01:12:43,590 --> 01:12:48,780 And that causes this.props.err to appear in our text box here, 1193 01:12:48,780 --> 01:12:52,100 which is exactly what happens when you type in something like this. 1194 01:12:52,100 --> 01:12:56,090 1195 01:12:56,090 --> 01:13:00,460 And, as you see, if we resubmit with an incorrect user, 1196 01:13:00,460 --> 01:13:04,690 it will update the error message correctly. 1197 01:13:04,690 --> 01:13:06,970 If we then change the username to be something 1198 01:13:06,970 --> 01:13:10,390 that's valid with the wrong password, then it updates correctly. 1199 01:13:10,390 --> 01:13:15,820 And now, if we update with the correct username and the correct password, 1200 01:13:15,820 --> 01:13:16,940 what happens? 1201 01:13:16,940 --> 01:13:19,370 Well, nothing actually happens. 1202 01:13:19,370 --> 01:13:24,561 Well, we get a JSON parse error, but nothing that's supposed to happen 1203 01:13:24,561 --> 01:13:25,060 happens. 1204 01:13:25,060 --> 01:13:28,210 1205 01:13:28,210 --> 01:13:34,060 So let's actually just fix that JSON parse error by restarting the server, 1206 01:13:34,060 --> 01:13:37,130 because I changed it and didn't restart it. 1207 01:13:37,130 --> 01:13:44,080 So now, if we refresh with the correct username and password combo, 1208 01:13:44,080 --> 01:13:45,820 nothing actually happens. 1209 01:13:45,820 --> 01:13:47,530 And why is that? 1210 01:13:47,530 --> 01:13:52,600 Well, we could look through everything again, but what ends up happening 1211 01:13:52,600 --> 01:13:54,370 is the token updates. 1212 01:13:54,370 --> 01:13:57,120 The token gets passed as a prop to this class and what happens? 1213 01:13:57,120 --> 01:13:59,860 Well, we don't actually look for the token. 1214 01:13:59,860 --> 01:14:03,010 And so maybe we should add some sort of listener 1215 01:14:03,010 --> 01:14:07,030 that says, hey, if we get a new token, then maybe we 1216 01:14:07,030 --> 01:14:10,040 should navigate to our main screen. 1217 01:14:10,040 --> 01:14:11,230 And so how might we do that? 1218 01:14:11,230 --> 01:14:16,750 Is there a particular way that we can have a React class listen to its props 1219 01:14:16,750 --> 01:14:18,785 and do something when it's changed? 1220 01:14:18,785 --> 01:14:19,410 Well, there is. 1221 01:14:19,410 --> 01:14:24,850 There's a React lifecycle method called componentWillRecieveProps. 1222 01:14:24,850 --> 01:14:28,020 1223 01:14:28,020 --> 01:14:31,840 And it's invoked with whatever the next props are. 1224 01:14:31,840 --> 01:14:43,370 And we can just do, if the nextProps.token exists, 1225 01:14:43,370 --> 01:14:49,506 then we can do this.props.navigation.navigate, 1226 01:14:49,506 --> 01:14:51,130 and we can navigate to our main screen. 1227 01:14:51,130 --> 01:14:54,990 1228 01:14:54,990 --> 01:14:59,945 And now we're actually listening for the token. 1229 01:14:59,945 --> 01:15:09,730 And when we receive it, we are logged in correctly. 1230 01:15:09,730 --> 01:15:13,082 So now we've gone ahead and implemented Redux 1231 01:15:13,082 --> 01:15:14,790 to handle all of our asynchronous actions 1232 01:15:14,790 --> 01:15:17,160 and our asynchronous logic in our contacts app. 1233 01:15:17,160 --> 01:15:21,321 1234 01:15:21,321 --> 01:15:21,820 Cool. 1235 01:15:21,820 --> 01:15:25,180 So now I'm going to close this app and reopen it. 1236 01:15:25,180 --> 01:15:26,650 And what happens? 1237 01:15:26,650 --> 01:15:29,905 Well, I'm logged out again, which is unideal, 1238 01:15:29,905 --> 01:15:32,530 because presumably I'm going to be the only one using my phone, 1239 01:15:32,530 --> 01:15:34,440 and an app like Facebook doesn't make me log 1240 01:15:34,440 --> 01:15:36,550 in every single time I reopen the app. 1241 01:15:36,550 --> 01:15:41,230 So how might we go ahead and store the state of our app 1242 01:15:41,230 --> 01:15:42,490 to persist throughout closing? 1243 01:15:42,490 --> 01:15:45,280 1244 01:15:45,280 --> 01:15:50,200 Well, now that our app basically looks to our Redux store 1245 01:15:50,200 --> 01:15:52,900 in order to know what we should be rendering, 1246 01:15:52,900 --> 01:15:55,580 our app is basically just a pure function of our store. 1247 01:15:55,580 --> 01:16:02,095 And so, basically, all of the things that we need 1248 01:16:02,095 --> 01:16:03,220 can be stored in the state. 1249 01:16:03,220 --> 01:16:05,740 And if we can persist that store, then we 1250 01:16:05,740 --> 01:16:12,670 can reload the app into its current state even after it's been closed. 1251 01:16:12,670 --> 01:16:14,470 So there's a few ways to do this. 1252 01:16:14,470 --> 01:16:18,110 React Native provides this API called AsyncStorage, 1253 01:16:18,110 --> 01:16:20,196 which is a persistent data store. 1254 01:16:20,196 --> 01:16:21,820 But if you read the docs, it says this. 1255 01:16:21,820 --> 01:16:23,710 "Use an abstraction on top of AsyncStorage 1256 01:16:23,710 --> 01:16:26,350 instead of using it directly for anything more than light usage 1257 01:16:26,350 --> 01:16:27,700 since it operates globally." 1258 01:16:27,700 --> 01:16:30,430 Or, in other words, we should probably stay away from that 1259 01:16:30,430 --> 01:16:32,810 if we want to preserve our sanity. 1260 01:16:32,810 --> 01:16:35,300 And so what might we want to do instead? 1261 01:16:35,300 --> 01:16:38,230 Well, it turns out there are abstractions on top of AsyncStorage, 1262 01:16:38,230 --> 01:16:40,900 one of which is called redux-persist. 1263 01:16:40,900 --> 01:16:44,290 So what this does is it abstracts out the storage of the store 1264 01:16:44,290 --> 01:16:46,090 into AsyncStorage for us. 1265 01:16:46,090 --> 01:16:48,670 So we don't have to handle anything in AsyncStorage. 1266 01:16:48,670 --> 01:16:51,457 Instead, we just use this library that does all that for us. 1267 01:16:51,457 --> 01:16:53,040 So it gives us a few different things. 1268 01:16:53,040 --> 01:16:56,350 It gives us this thing called a persistStore, a persistReducer, 1269 01:16:56,350 --> 01:16:59,260 and a persistGate. 1270 01:16:59,260 --> 01:17:04,360 And what that does is it automatically stores this data every single change. 1271 01:17:04,360 --> 01:17:08,320 It will automatically rehydrate our app when it's reopened. 1272 01:17:08,320 --> 01:17:13,300 And so, in other words, it takes what was stored in AsyncStorage 1273 01:17:13,300 --> 01:17:18,152 and puts that into Redux when the app is reopened. 1274 01:17:18,152 --> 01:17:20,110 And it all displays some sort of loading screen 1275 01:17:20,110 --> 01:17:22,120 while it's waiting for that to happen. 1276 01:17:22,120 --> 01:17:24,070 And so the docs are here, and let's go look 1277 01:17:24,070 --> 01:17:26,100 at how to implement that in our app. 1278 01:17:26,100 --> 01:17:28,860 1279 01:17:28,860 --> 01:17:30,360 So it tells us how to install it. 1280 01:17:30,360 --> 01:17:36,091 We should just run npm install react-persist, or redux-persist. 1281 01:17:36,091 --> 01:17:37,340 So let's go ahead and do that. 1282 01:17:37,340 --> 01:17:39,690 And while we're waiting, we can read on. 1283 01:17:39,690 --> 01:17:42,055 Basically, what it says is, we're going to pass 1284 01:17:42,055 --> 01:17:46,370 it some sort of config where we tell it what storage to use. 1285 01:17:46,370 --> 01:17:50,460 And since Redux is something you can use in React Web and React Native, 1286 01:17:50,460 --> 01:17:52,290 we need to know whether we should be using 1287 01:17:52,290 --> 01:17:58,074 something like a local storage for web, or AsyncStorage in our case. 1288 01:17:58,074 --> 01:18:00,990 We can go ahead and create this thing called a persistedReducer, which 1289 01:18:00,990 --> 01:18:05,370 is basically the same thing as a normal reducer but it persists for us. 1290 01:18:05,370 --> 01:18:09,000 We can create the store with that persistedReducer. 1291 01:18:09,000 --> 01:18:11,460 And we can create this thing called a persister, which 1292 01:18:11,460 --> 01:18:16,200 is a persistent version of that store. 1293 01:18:16,200 --> 01:18:20,370 So let's go ahead and just copy all of this and add this to our store. 1294 01:18:20,370 --> 01:18:27,860 1295 01:18:27,860 --> 01:18:30,250 So let me just copy and paste that there. 1296 01:18:30,250 --> 01:18:34,340 1297 01:18:34,340 --> 01:18:36,500 And let's modify a few things so that it works 1298 01:18:36,500 --> 01:18:38,162 with exactly what we're trying to do. 1299 01:18:38,162 --> 01:18:40,990 1300 01:18:40,990 --> 01:18:49,710 So let's delete this comment and move these two up here. 1301 01:18:49,710 --> 01:18:50,210 All right. 1302 01:18:50,210 --> 01:18:53,800 So now we've gone ahead and imported persistStore and persistReducer. 1303 01:18:53,800 --> 01:18:56,690 We've imported storage, which is what we want. 1304 01:18:56,690 --> 01:19:01,740 Rather than importing rootReducer, we're importing our own reducer. 1305 01:19:01,740 --> 01:19:03,709 We've created this thing called persistConfig. 1306 01:19:03,709 --> 01:19:06,500 You can read through the docs to figure out exactly what this does, 1307 01:19:06,500 --> 01:19:11,030 but basically we create a unique key for the only persistent config 1308 01:19:11,030 --> 01:19:13,940 that we have, and we're telling it what storage device to use. 1309 01:19:13,940 --> 01:19:17,620 And it's going to default to AsyncStorage for our use case 1310 01:19:17,620 --> 01:19:20,404 since we're using React Native. 1311 01:19:20,404 --> 01:19:22,820 We then create this thing called a persistedReducer, which 1312 01:19:22,820 --> 01:19:24,230 is persistReducer. 1313 01:19:24,230 --> 01:19:27,973 We pass in our persistConfig, and we're going to pass in our reducer. 1314 01:19:27,973 --> 01:19:31,630 1315 01:19:31,630 --> 01:19:33,160 Let's move this down for now. 1316 01:19:33,160 --> 01:19:37,570 1317 01:19:37,570 --> 01:19:39,260 Let's comment out all this stuff. 1318 01:19:39,260 --> 01:19:42,050 1319 01:19:42,050 --> 01:19:44,750 And then what we do is we create a store, which 1320 01:19:44,750 --> 01:19:47,810 is basically what we've already done. 1321 01:19:47,810 --> 01:19:50,930 So we can delete this line, delete this line. 1322 01:19:50,930 --> 01:19:56,300 We can create a persister, which is basically wrapping our store 1323 01:19:56,300 --> 01:19:59,150 in this API called persistStore. 1324 01:19:59,150 --> 01:20:02,480 And then, rather than wrapping that in a thunk, let's just return 1325 01:20:02,480 --> 01:20:04,910 the store in the persister. 1326 01:20:04,910 --> 01:20:11,420 So let's export the store and the persister. 1327 01:20:11,420 --> 01:20:15,770 Or we can just use the syntax that we're familiar with by doing-- 1328 01:20:15,770 --> 01:20:22,220 export the const store is this, and export the const persister is this. 1329 01:20:22,220 --> 01:20:27,020 So, basically, what I just did is I cut and paste the API usage. 1330 01:20:27,020 --> 01:20:29,060 I read through the docs beforehand, but I 1331 01:20:29,060 --> 01:20:32,900 happen to know that the key here is it refers 1332 01:20:32,900 --> 01:20:36,050 to, if we want to use multiple different persisters in our app, we can. 1333 01:20:36,050 --> 01:20:38,896 But since we're only using one, we can just hard code the key here. 1334 01:20:38,896 --> 01:20:40,520 We tell it which storage device to use. 1335 01:20:40,520 --> 01:20:43,550 In our case, it's AsyncStorage. 1336 01:20:43,550 --> 01:20:45,200 We create the persistedReducer. 1337 01:20:45,200 --> 01:20:48,080 1338 01:20:48,080 --> 01:20:52,650 This here should actually be persistReducer, I believe. 1339 01:20:52,650 --> 01:21:03,941 So we should use the persistedReducer here, which I spelled incorrectly. 1340 01:21:03,941 --> 01:21:06,890 1341 01:21:06,890 --> 01:21:08,772 We still are using our thunk middleware. 1342 01:21:08,772 --> 01:21:10,730 And then we're also exporting this thing called 1343 01:21:10,730 --> 01:21:15,290 a persister, which we need to pass into our persistent gate. 1344 01:21:15,290 --> 01:21:19,294 And our PersistGate, what it's doing is it's waiting for our store to rehydrate 1345 01:21:19,294 --> 01:21:21,460 and displaying a loading screen while we're waiting. 1346 01:21:21,460 --> 01:21:25,469 And, in this case, we're just going to pass in null for that. 1347 01:21:25,469 --> 01:21:28,010 So rather than retyping, I'm just going to cut and paste that 1348 01:21:28,010 --> 01:21:29,572 into our application. 1349 01:21:29,572 --> 01:21:33,640 1350 01:21:33,640 --> 01:21:42,090 So let's have a PersistGate here and import PersistGate from this library. 1351 01:21:42,090 --> 01:21:46,170 1352 01:21:46,170 --> 01:21:49,640 And now, rather than importing store from our Redux store, 1353 01:21:49,640 --> 01:21:51,890 we had a couple of named exports. 1354 01:21:51,890 --> 01:21:54,238 We had a store and we had a persister. 1355 01:21:54,238 --> 01:21:57,450 1356 01:21:57,450 --> 01:22:02,100 And those correspond to the store here and the persister here. 1357 01:22:02,100 --> 01:22:07,619 So I just did a very whirlwind cut and paste from their docs, 1358 01:22:07,619 --> 01:22:09,910 but this default configuration should work fine for us. 1359 01:22:09,910 --> 01:22:17,110 1360 01:22:17,110 --> 01:22:19,810 So let's now refresh. 1361 01:22:19,810 --> 01:22:22,490 And look, we're back in. 1362 01:22:22,490 --> 01:22:24,520 If we refresh again, we're back in. 1363 01:22:24,520 --> 01:22:25,220 Why is that? 1364 01:22:25,220 --> 01:22:29,680 Well, it's because we're storing the token in our Redux store. 1365 01:22:29,680 --> 01:22:36,310 And when we refresh the app, then it rehydrates our store 1366 01:22:36,310 --> 01:22:39,220 so that the token is still in our Redux store. 1367 01:22:39,220 --> 01:22:43,840 And then our LoginScreen sees that the token is in our store 1368 01:22:43,840 --> 01:22:46,310 and brings us to this contacts page. 1369 01:22:46,310 --> 01:22:49,450 And if you see really quickly, there's a flash of our login screen 1370 01:22:49,450 --> 01:22:53,740 before, because what we're doing there is we're looking-- 1371 01:22:53,740 --> 01:22:58,710 when it receives new props, that's when it does the navigation. 1372 01:22:58,710 --> 01:23:01,150 But everything is working exactly as expected. 1373 01:23:01,150 --> 01:23:03,830 1374 01:23:03,830 --> 01:23:07,940 So now we have a few components that are listening to our application 1375 01:23:07,940 --> 01:23:09,850 state via this connect. 1376 01:23:09,850 --> 01:23:12,490 And we also have a bunch of components that are just 1377 01:23:12,490 --> 01:23:15,130 receiving props and displaying them. 1378 01:23:15,130 --> 01:23:21,520 And so how can we go ahead and determine when to use which? 1379 01:23:21,520 --> 01:23:23,980 So the actual terms behind these components 1380 01:23:23,980 --> 01:23:30,640 are container and presentational, where these containers 1381 01:23:30,640 --> 01:23:34,300 are aware of the Redux state, whereas the presentational components are only 1382 01:23:34,300 --> 01:23:36,400 aware of the props that are passed. 1383 01:23:36,400 --> 01:23:39,040 And as our application grows in size and complexity, 1384 01:23:39,040 --> 01:23:41,200 we probably don't want every single class 1385 01:23:41,200 --> 01:23:44,500 to be listening to our application state. 1386 01:23:44,500 --> 01:23:49,360 And likewise, we don't want every single component 1387 01:23:49,360 --> 01:23:51,730 to just wait for the props that are passed down, 1388 01:23:51,730 --> 01:23:54,980 because that was part of the reason that we moved to Redux in the first place. 1389 01:23:54,980 --> 01:23:57,190 1390 01:23:57,190 --> 01:24:01,840 And so we need to start to figure out a way in order to do this. 1391 01:24:01,840 --> 01:24:04,210 And there's a great article written by Dan Abramov, 1392 01:24:04,210 --> 01:24:07,750 so the same guy who wrote Redux Thunk. 1393 01:24:07,750 --> 01:24:10,570 Also, he's the same guy who actually created Redux. 1394 01:24:10,570 --> 01:24:17,260 He has a great blog post on what the URL says are smart and dumb components, 1395 01:24:17,260 --> 01:24:20,800 but what he actually ended up renaming to container and presentational. 1396 01:24:20,800 --> 01:24:26,150 But, generally, my heuristic is, if I have a very, very simple component, 1397 01:24:26,150 --> 01:24:30,190 like a row in a list or something like that, that should not 1398 01:24:30,190 --> 01:24:31,650 be aware of the application state. 1399 01:24:31,650 --> 01:24:37,240 It should be looking to its parent to pass the data down to it. 1400 01:24:37,240 --> 01:24:39,190 Whereas, if I have a whole screen-- 1401 01:24:39,190 --> 01:24:43,690 where maybe the screen does care about the Redux state 1402 01:24:43,690 --> 01:24:45,940 because it needs to handle things like navigation, 1403 01:24:45,940 --> 01:24:48,640 or needs to handle things like listening to the state 1404 01:24:48,640 --> 01:24:54,340 and passing it down to its child components-- 1405 01:24:54,340 --> 01:25:00,610 that's when something should then listen to the Redux state. 1406 01:25:00,610 --> 01:25:04,180 And so I highly encourage you to go read this blog post, 1407 01:25:04,180 --> 01:25:06,610 and Dan Abramov will give his explanation 1408 01:25:06,610 --> 01:25:09,730 on what he thinks should be a container and what he thinks 1409 01:25:09,730 --> 01:25:11,800 should be a presentational component. 1410 01:25:11,800 --> 01:25:15,670 One thing to note is that container components 1411 01:25:15,670 --> 01:25:18,520 can have children that are presentational components, 1412 01:25:18,520 --> 01:25:21,034 and that those children can also be container components. 1413 01:25:21,034 --> 01:25:23,200 And those children can be presentational components. 1414 01:25:23,200 --> 01:25:26,500 It doesn't necessarily mean that, once you have a presentational component, 1415 01:25:26,500 --> 01:25:31,260 all of its children have to be presentational. 1416 01:25:31,260 --> 01:25:34,930 And so we've added Redux to a relatively simple app, 1417 01:25:34,930 --> 01:25:39,820 but the question still remains, did we actually need Redux there? 1418 01:25:39,820 --> 01:25:44,870 And so what Redux does is it helps apps scale, but it also adds complexity. 1419 01:25:44,870 --> 01:25:51,520 So, as you remembered, when we wanted to add a simple login action, 1420 01:25:51,520 --> 01:25:55,030 we actually had to touch something like five different files. 1421 01:25:55,030 --> 01:26:00,010 First we had to open up our Redux actions file 1422 01:26:00,010 --> 01:26:02,650 and create a couple of different action types, 1423 01:26:02,650 --> 01:26:05,920 and also create an async action creator. 1424 01:26:05,920 --> 01:26:13,750 Then we also had to open our store to ensure that Redux knew what we were 1425 01:26:13,750 --> 01:26:15,530 doing when we return an async creator. 1426 01:26:15,530 --> 01:26:20,170 We had to actually add the middleware from Redux Thunk 1427 01:26:20,170 --> 01:26:26,350 to know how to handle asynchronous action creators. 1428 01:26:26,350 --> 01:26:31,180 Then we also had to listen for those actions in our Redux reducer. 1429 01:26:31,180 --> 01:26:35,692 And so we had to add these lines that said, hey, if the login worked, 1430 01:26:35,692 --> 01:26:37,150 let's update our state accordingly. 1431 01:26:37,150 --> 01:26:39,927 If the login didn't work, let's update our state accordingly. 1432 01:26:39,927 --> 01:26:41,010 And we still weren't done. 1433 01:26:41,010 --> 01:26:45,580 We also had to update our actual components. 1434 01:26:45,580 --> 01:26:52,420 So in screens/Login, we also had to update this 1435 01:26:52,420 --> 01:26:55,750 with our connect function here, that listen 1436 01:26:55,750 --> 01:26:58,510 to particular parts of the state. 1437 01:26:58,510 --> 01:27:04,820 And we also had to bind our action creator here and use that in our class. 1438 01:27:04,820 --> 01:27:06,940 And so it's a very non-trivial amount of work 1439 01:27:06,940 --> 01:27:11,440 in order to add this simple Redux action. 1440 01:27:11,440 --> 01:27:16,120 But it does definitely add to our scalability. 1441 01:27:16,120 --> 01:27:19,130 But sometimes the complexity isn't necessarily worth it. 1442 01:27:19,130 --> 01:27:23,650 And so you have to ask yourself some questions, 1443 01:27:23,650 --> 01:27:25,690 like is the complexity actually worth it? 1444 01:27:25,690 --> 01:27:27,730 Have I run into some pain points yet? 1445 01:27:27,730 --> 01:27:31,100 And so, generally, what I do is I do as much as I can with local component 1446 01:27:31,100 --> 01:27:32,170 state. 1447 01:27:32,170 --> 01:27:36,450 And then if I end up running into scalability issues, 1448 01:27:36,450 --> 01:27:38,010 I then move to Redux. 1449 01:27:38,010 --> 01:27:41,360 And it doesn't necessarily mean that I implement my entire app then 1450 01:27:41,360 --> 01:27:43,414 run into the paint points and start adding Redux. 1451 01:27:43,414 --> 01:27:44,830 It might be just some forethought. 1452 01:27:44,830 --> 01:27:49,762 Maybe I'm going to start planning as if I'm using only component state, 1453 01:27:49,762 --> 01:27:51,970 and then I see that I'm going to run into pain points 1454 01:27:51,970 --> 01:27:54,920 and then know that I should use Redux before implementing. 1455 01:27:54,920 --> 01:27:57,190 And, again, what are these pain points? 1456 01:27:57,190 --> 01:28:00,220 Well, maybe you're forgetting to pass a prop. 1457 01:28:00,220 --> 01:28:03,130 Maybe you're directly managing a deeply nested state 1458 01:28:03,130 --> 01:28:06,460 when you don't necessarily need to be. 1459 01:28:06,460 --> 01:28:09,430 Maybe you have a lot of duplicate information in your state, 1460 01:28:09,430 --> 01:28:11,950 where you can just instead put it in your Redux store 1461 01:28:11,950 --> 01:28:14,590 and listen to those values when you need to. 1462 01:28:14,590 --> 01:28:17,020 Maybe you're not updating all the dependent props. 1463 01:28:17,020 --> 01:28:22,780 A way you can fix that in Redux is just by having all of those props 1464 01:28:22,780 --> 01:28:26,489 listened to in the reducer when you're going to update the state. 1465 01:28:26,489 --> 01:28:28,780 Maybe you have a component with a large number of props 1466 01:28:28,780 --> 01:28:31,200 because it needs to pass as props to the children, 1467 01:28:31,200 --> 01:28:34,030 and we can just bypass that by using that connect function 1468 01:28:34,030 --> 01:28:36,670 and hopping down to the children very low in the tree 1469 01:28:36,670 --> 01:28:39,666 and listening in to the Redux store. 1470 01:28:39,666 --> 01:28:42,540 And maybe you have some uncertainty where a piece of data is managed. 1471 01:28:42,540 --> 01:28:44,590 And the nice thing about Redux here is if I 1472 01:28:44,590 --> 01:28:47,500 want to change some logic in how the store is updated, 1473 01:28:47,500 --> 01:28:49,360 I know I should be looking at my reducer. 1474 01:28:49,360 --> 01:28:54,174 If I want to add a new or change the way that an asynchronous action is fired, 1475 01:28:54,174 --> 01:28:56,090 I know I should be looking at my actions file. 1476 01:28:56,090 --> 01:29:00,390 So it's very easy for us to know exactly where the logic for a particular thing 1477 01:29:00,390 --> 01:29:02,740 lives. 1478 01:29:02,740 --> 01:29:04,690 So that ends our discussion on Redux. 1479 01:29:04,690 --> 01:29:08,230 Feel free to reach out in Slack if you are curious about when 1480 01:29:08,230 --> 01:29:09,300 you should use Redux. 1481 01:29:09,300 --> 01:29:11,050 But let's actually talk about some tooling 1482 01:29:11,050 --> 01:29:16,067 that might help our JavaScript writing a little bit easier. 1483 01:29:16,067 --> 01:29:17,650 So there are a lot of tools out there. 1484 01:29:17,650 --> 01:29:20,000 There are a lot of JavaScript libraries out there. 1485 01:29:20,000 --> 01:29:22,984 Some people say there are too many JavaScript libraries. 1486 01:29:22,984 --> 01:29:25,900 But you can sift through the noise and find a few really, really great 1487 01:29:25,900 --> 01:29:27,550 JavaScript tools. 1488 01:29:27,550 --> 01:29:29,520 One of them, of course, is npm. 1489 01:29:29,520 --> 01:29:32,560 Npm is something that we've used multiple times today. 1490 01:29:32,560 --> 01:29:38,710 We've done npm install with our dependencies. 1491 01:29:38,710 --> 01:29:42,030 And that, again, stores things in our package.json 1492 01:29:42,030 --> 01:29:45,880 and allows us to keep track of all the dependencies for everybody 1493 01:29:45,880 --> 01:29:48,130 who then later wants to use our application. 1494 01:29:48,130 --> 01:29:51,559 They can just run npm install and have everything that they need. 1495 01:29:51,559 --> 01:29:53,350 We've talked about this thing called Babel, 1496 01:29:53,350 --> 01:29:57,130 which allows us to write JavaScript as if it has all of the language 1497 01:29:57,130 --> 01:30:00,820 features that we need and then transpile down to JavaScript 1498 01:30:00,820 --> 01:30:02,380 that all browsers will understand. 1499 01:30:02,380 --> 01:30:06,500 And so we've talked about these terms called ES6, ES7, ES.Next, 1500 01:30:06,500 --> 01:30:09,640 and we've been using a lot of those things, like our class properties. 1501 01:30:09,640 --> 01:30:13,780 And Babel's running behind the hood to transpile that all down to a language 1502 01:30:13,780 --> 01:30:18,070 that anything that understands basic JavaScript will understand. 1503 01:30:18,070 --> 01:30:22,360 There's this thing called ESM, which is a JavaScript library that you can 1504 01:30:22,360 --> 01:30:26,140 install via npm install at standard /esm. 1505 01:30:26,140 --> 01:30:29,260 And what that allows us to do is use our import statements 1506 01:30:29,260 --> 01:30:31,210 and our export statements in Node. 1507 01:30:31,210 --> 01:30:33,910 So you've seen me, when I'm doing my quick examples, 1508 01:30:33,910 --> 01:30:36,580 changing my syntax to something like require. 1509 01:30:36,580 --> 01:30:39,280 But if we use this library here, you can actually just 1510 01:30:39,280 --> 01:30:40,550 use import directly into Node. 1511 01:30:40,550 --> 01:30:42,550 And I'm not going to demonstrate how to do that, 1512 01:30:42,550 --> 01:30:46,690 but I encourage you to go check that out if it's something that interests you. 1513 01:30:46,690 --> 01:30:50,890 We talked about the Chrome devtools and how we can use those to debug. 1514 01:30:50,890 --> 01:30:54,220 And we'll talk about, next lecture, how we can use those to actually look 1515 01:30:54,220 --> 01:30:56,650 at things, including performance. 1516 01:30:56,650 --> 01:30:59,030 There are also things like React and Redux devtools. 1517 01:30:59,030 --> 01:31:02,440 We already talked about React devtools in our debugging lecture. 1518 01:31:02,440 --> 01:31:06,250 But there also exists Redux devtools, which allows you to replay actions 1519 01:31:06,250 --> 01:31:08,000 or see what actions have happened. 1520 01:31:08,000 --> 01:31:13,480 And I encourage you to go look at that if you want to debug your Redux. 1521 01:31:13,480 --> 01:31:17,590 We're going to talk about, later today, this thing called ESLint and Prettier. 1522 01:31:17,590 --> 01:31:20,069 And then there's also this thing called Flow or TypeScript. 1523 01:31:20,069 --> 01:31:22,360 Those are both things that allow us to statically check 1524 01:31:22,360 --> 01:31:25,060 the types of all of our functions. 1525 01:31:25,060 --> 01:31:28,960 And so it helps us eliminate bugs where maybe we 1526 01:31:28,960 --> 01:31:32,020 changed the function prototype somewhere but forgot to update 1527 01:31:32,020 --> 01:31:33,450 wherever we use those functions. 1528 01:31:33,450 --> 01:31:36,070 And so, when we want to really scale up our application, 1529 01:31:36,070 --> 01:31:38,740 we might consider using something like Flow or TypeScript 1530 01:31:38,740 --> 01:31:42,400 to statically check the types to make sure that no bugs are 1531 01:31:42,400 --> 01:31:44,725 created as we change things around. 1532 01:31:44,725 --> 01:31:49,030 And so let's actually talk about two tools in particular today, those being 1533 01:31:49,030 --> 01:31:50,900 ESLint and Prettier. 1534 01:31:50,900 --> 01:31:53,620 And so what is ESLint? 1535 01:31:53,620 --> 01:31:57,980 You may have seen it floating around on open source projects that you use. 1536 01:31:57,980 --> 01:32:00,250 You may have seen me try to use it earlier today as I 1537 01:32:00,250 --> 01:32:02,590 was debugging a syntax error. 1538 01:32:02,590 --> 01:32:05,500 But what this is is it's a fully pluggable tool 1539 01:32:05,500 --> 01:32:08,890 for identifying and reporting on patterns in JavaScript. 1540 01:32:08,890 --> 01:32:12,610 Or, in other words, it allows us to enforce some code style rules 1541 01:32:12,610 --> 01:32:14,650 and statically analyze our code to ensure 1542 01:32:14,650 --> 01:32:18,280 that it complies with those rules. 1543 01:32:18,280 --> 01:32:23,001 What this helps us do is it helps ensure the style consistency across our code 1544 01:32:23,001 --> 01:32:23,500 base. 1545 01:32:23,500 --> 01:32:26,980 And this is great if we have maybe a hundred different people using 1546 01:32:26,980 --> 01:32:29,620 the same code base, where maybe everybody writes JavaScript 1547 01:32:29,620 --> 01:32:31,240 slightly differently. 1548 01:32:31,240 --> 01:32:34,670 What we can do is we can use ESLint to yell at our developers and say, 1549 01:32:34,670 --> 01:32:38,170 hey, we should all be writing the same style JavaScript. 1550 01:32:38,170 --> 01:32:40,330 Maybe you should use this instead. 1551 01:32:40,330 --> 01:32:44,114 And so here's a link to it if you want to check it out. 1552 01:32:44,114 --> 01:32:45,930 And so how do we go ahead and set it up? 1553 01:32:45,930 --> 01:32:47,690 Well, first we'll need to install it. 1554 01:32:47,690 --> 01:32:49,815 So there are two different ways you can install it. 1555 01:32:49,815 --> 01:32:52,000 You can install it per particular project, which 1556 01:32:52,000 --> 01:32:55,160 means it's not installed on your computer as a whole, 1557 01:32:55,160 --> 01:32:58,592 like you can't just type eslint at a terminal. 1558 01:32:58,592 --> 01:33:01,550 But what it does is it allows you to use it in your particular project. 1559 01:33:01,550 --> 01:33:05,290 So if you want to use an npm script to ESLint. 1560 01:33:05,290 --> 01:33:07,009 Or if you want to-- 1561 01:33:07,009 --> 01:33:09,050 I'll show you a command later to use it directly. 1562 01:33:09,050 --> 01:33:12,130 You can install it for a particular library rather than 1563 01:33:12,130 --> 01:33:13,297 your whole computer. 1564 01:33:13,297 --> 01:33:14,380 Or you can do it globally. 1565 01:33:14,380 --> 01:33:19,120 You can just do npm install -g or --global, which allows you to just run 1566 01:33:19,120 --> 01:33:21,580 that command eslint anywhere. 1567 01:33:21,580 --> 01:33:24,520 So let's actually install it here. 1568 01:33:24,520 --> 01:33:27,445 And I'm going to install it only in this project. 1569 01:33:27,445 --> 01:33:34,300 So I'm going to npm install --save-dev, or capital D, eslint. 1570 01:33:34,300 --> 01:33:36,987 1571 01:33:36,987 --> 01:33:37,570 And then what? 1572 01:33:37,570 --> 01:33:38,980 We should create our own config. 1573 01:33:38,980 --> 01:33:43,190 And so we can do this by, again, two different ways. 1574 01:33:43,190 --> 01:33:48,110 If we installed it globally, we can just do eslint init. 1575 01:33:48,110 --> 01:33:49,990 I believe that should be --init. 1576 01:33:49,990 --> 01:33:51,880 Or we can do per project. 1577 01:33:51,880 --> 01:33:57,940 So we can do ./node_modules/.bin/eslint, which is where that eslint script 1578 01:33:57,940 --> 01:34:00,290 happens to live, and then initialize it. 1579 01:34:00,290 --> 01:34:03,250 So let's go ahead and do that. 1580 01:34:03,250 --> 01:34:12,850 I can do npm, or I can do ./node_modules/.bin/eslint, 1581 01:34:12,850 --> 01:34:15,745 and then --init. 1582 01:34:15,745 --> 01:34:17,620 And now it's going to ask me a few questions, 1583 01:34:17,620 --> 01:34:21,194 like how do I actually want to lint my code. 1584 01:34:21,194 --> 01:34:23,110 So let's answer some questions about my style. 1585 01:34:23,110 --> 01:34:24,510 Are we using ES6 features? 1586 01:34:24,510 --> 01:34:25,240 Yes, we are. 1587 01:34:25,240 --> 01:34:26,860 Are you using ES6 modules? 1588 01:34:26,860 --> 01:34:27,370 Yes, we are. 1589 01:34:27,370 --> 01:34:28,780 We're using the import. 1590 01:34:28,780 --> 01:34:31,090 Where is it going to run? 1591 01:34:31,090 --> 01:34:33,940 Technically neither, but browser is the more accurate one here. 1592 01:34:33,940 --> 01:34:36,100 Do we use CommonJS? 1593 01:34:36,100 --> 01:34:36,760 No, we don't. 1594 01:34:36,760 --> 01:34:38,360 You don't have to know what that is? 1595 01:34:38,360 --> 01:34:39,300 Do we use JSX? 1596 01:34:39,300 --> 01:34:40,450 Yes, in fact we do. 1597 01:34:40,450 --> 01:34:42,940 And in fact we also use React. 1598 01:34:42,940 --> 01:34:44,860 What style of indentation do I use? 1599 01:34:44,860 --> 01:34:46,590 I use spaces. 1600 01:34:46,590 --> 01:34:47,710 What quotes do I use? 1601 01:34:47,710 --> 01:34:49,135 I prefer single. 1602 01:34:49,135 --> 01:34:51,100 What line endings do we use? 1603 01:34:51,100 --> 01:34:53,080 I'm using Unix. 1604 01:34:53,080 --> 01:34:54,460 Do I require semicolons? 1605 01:34:54,460 --> 01:34:56,060 This one is of great debate. 1606 01:34:56,060 --> 01:34:57,480 I prefer not. 1607 01:34:57,480 --> 01:35:01,164 And what format do we want our config file to be in? 1608 01:35:01,164 --> 01:35:02,080 There's a few options. 1609 01:35:02,080 --> 01:35:04,280 I'm just going to use YAML here. 1610 01:35:04,280 --> 01:35:07,019 And then it's going to install the dependencies for me 1611 01:35:07,019 --> 01:35:08,560 and actually create this config file. 1612 01:35:08,560 --> 01:35:11,836 1613 01:35:11,836 --> 01:35:16,030 And as that installs, let's forge ahead. 1614 01:35:16,030 --> 01:35:19,750 So we can create our own config by answering that questionnaire. 1615 01:35:19,750 --> 01:35:22,720 We'll see exactly how that looks in a second. 1616 01:35:22,720 --> 01:35:25,600 Or we can actually steal a config from somebody else. 1617 01:35:25,600 --> 01:35:27,730 We can extend an existing config. 1618 01:35:27,730 --> 01:35:31,240 So there's this thing called Airbnb's JavaScript style 1619 01:35:31,240 --> 01:35:35,720 guide, which is a very, very popular one that a lot of people like to use. 1620 01:35:35,720 --> 01:35:39,520 And there's also the one I prefer, which is the one 1621 01:35:39,520 --> 01:35:43,120 that I use at our company called Kensho, and it's the style guide 1622 01:35:43,120 --> 01:35:47,060 that I've been loosely following as I type and lecture. 1623 01:35:47,060 --> 01:35:48,940 And so let's actually-- 1624 01:35:48,940 --> 01:35:53,140 now that this is done installing, let's see what happened. 1625 01:35:53,140 --> 01:35:57,730 I can see that if I do ls -a, I can see that this file called 1626 01:35:57,730 --> 01:35:59,970 eslint.yml got created. 1627 01:35:59,970 --> 01:36:02,330 And so let's take a look at that real quick. 1628 01:36:02,330 --> 01:36:06,340 1629 01:36:06,340 --> 01:36:12,760 I see a lot of key-value pairs that actually correspond 1630 01:36:12,760 --> 01:36:15,070 to the questionnaire that I just did. 1631 01:36:15,070 --> 01:36:18,040 Env browser true is because we answered that we 1632 01:36:18,040 --> 01:36:19,480 said we're running in a browser. 1633 01:36:19,480 --> 01:36:22,619 ES6 true because we answered that we are, in fact, using ES6. 1634 01:36:22,619 --> 01:36:24,660 And you see a bunch of other things, like plugins 1635 01:36:24,660 --> 01:36:28,520 react, rules indent, blah blah blah. 1636 01:36:28,520 --> 01:36:33,880 And I can run this by doing ./node_modules/.bin/eslint. 1637 01:36:33,880 --> 01:36:39,400 1638 01:36:39,400 --> 01:36:43,850 And then I can run it on any file I want, so let's just do API. 1639 01:36:43,850 --> 01:36:47,417 And we see a parsing error because there's an unexpected token. 1640 01:36:47,417 --> 01:36:48,250 What does that mean? 1641 01:36:48,250 --> 01:36:51,420 1642 01:36:51,420 --> 01:36:53,900 Well, it turns out-- 1643 01:36:53,900 --> 01:36:59,120 and you can see I have a extension in my text editor 1644 01:36:59,120 --> 01:37:00,990 that automatically lints as I write. 1645 01:37:00,990 --> 01:37:04,790 And it's telling me that this is an unexpected token, 1646 01:37:04,790 --> 01:37:08,650 that this syntax actually isn't really supported by JavaScript yet. 1647 01:37:08,650 --> 01:37:11,530 And so maybe we should add some ESLint rules that 1648 01:37:11,530 --> 01:37:15,080 allow us to use these future things. 1649 01:37:15,080 --> 01:37:17,740 One way to do that is to use somebody else's config. 1650 01:37:17,740 --> 01:37:21,020 So I'm actually going to use the Kensho config because it's 1651 01:37:21,020 --> 01:37:23,960 the one that I tend to follow. 1652 01:37:23,960 --> 01:37:28,640 And the way to install that is to install this, and then 1653 01:37:28,640 --> 01:37:33,650 use an eslint.yml file that just has this. 1654 01:37:33,650 --> 01:37:45,290 So let me do npm install, npm install save-dev eslint-config-kensho. 1655 01:37:45,290 --> 01:37:50,230 1656 01:37:50,230 --> 01:37:56,330 And that's going to go ahead and install that particular ESLint rule set for me. 1657 01:37:56,330 --> 01:37:59,390 And then what I can do is I can use that. 1658 01:37:59,390 --> 01:38:01,370 Well, now nothing is going to happen if I just 1659 01:38:01,370 --> 01:38:06,330 try it because our ESLint configuration file still has all of the old stuff. 1660 01:38:06,330 --> 01:38:07,820 So let me just update that. 1661 01:38:07,820 --> 01:38:10,430 1662 01:38:10,430 --> 01:38:12,830 Now, instead of having all of these things, 1663 01:38:12,830 --> 01:38:21,230 I'm just going to say extend the Kensho one. 1664 01:38:21,230 --> 01:38:25,040 So now, if I do npx eslint-- 1665 01:38:25,040 --> 01:38:32,422 so npx is a shortcut for ./node_modules/.bin/eslint. 1666 01:38:32,422 --> 01:38:34,130 It's a shortcut for this, so I could just 1667 01:38:34,130 --> 01:38:39,740 do npx eslint if I happen to be running Node 5 or above. 1668 01:38:39,740 --> 01:38:41,600 And I can just lint that API file. 1669 01:38:41,600 --> 01:38:46,850 And I'm going to run into this, that says, cannot find module prettier. 1670 01:38:46,850 --> 01:38:49,199 And what exactly is Prettier? 1671 01:38:49,199 --> 01:38:51,240 So it's something that I mentioned earlier today. 1672 01:38:51,240 --> 01:38:56,550 1673 01:38:56,550 --> 01:38:59,340 And it's a very opinionated code formatter. 1674 01:38:59,340 --> 01:39:01,180 What the heck does that mean? 1675 01:39:01,180 --> 01:39:05,370 Well, it will actually rewrite your files to a specified code style. 1676 01:39:05,370 --> 01:39:07,440 So rather than ESLint complaining and saying, 1677 01:39:07,440 --> 01:39:11,020 hey, your code isn't styled correctly, Prettier will actually say, 1678 01:39:11,020 --> 01:39:13,080 hey, your code isn't styled correctly and I'm 1679 01:39:13,080 --> 01:39:16,230 going to rewrite it to style it correctly for you. 1680 01:39:16,230 --> 01:39:20,100 And what it can do is it can actually integrate with ESLint 1681 01:39:20,100 --> 01:39:23,490 so that I can automatically lint a file and also 1682 01:39:23,490 --> 01:39:25,864 rewrite it to adhere to a particular code standard. 1683 01:39:25,864 --> 01:39:27,780 And so let's go ahead and install that for us. 1684 01:39:27,780 --> 01:39:31,820 1685 01:39:31,820 --> 01:39:35,870 And what that does is it allows us to use ESLint and have that automatically 1686 01:39:35,870 --> 01:39:37,880 fix. 1687 01:39:37,880 --> 01:39:40,130 And so if you have an ESLint config that tells 1688 01:39:40,130 --> 01:39:43,160 it to use Prettier to rewrite files, you can 1689 01:39:43,160 --> 01:39:47,910 pass a flag that says --fix, telling ESLint to rewrite your file for you so 1690 01:39:47,910 --> 01:39:51,960 that it adheres to the code standards. 1691 01:39:51,960 --> 01:39:54,950 So now, again, let's run npx eslint on API. 1692 01:39:54,950 --> 01:39:57,900 1693 01:39:57,900 --> 01:39:58,550 Oh. 1694 01:39:58,550 --> 01:40:02,269 I don't happen to have any errors in there, so let me add one. 1695 01:40:02,269 --> 01:40:05,650 1696 01:40:05,650 --> 01:40:10,480 So maybe I'm going to export a const called poorlyFormatted. 1697 01:40:10,480 --> 01:40:13,810 1698 01:40:13,810 --> 01:40:24,950 And it's going to be something like unused variable. 1699 01:40:24,950 --> 01:40:30,170 And then it's going to just return unused variable. 1700 01:40:30,170 --> 01:40:33,586 1701 01:40:33,586 --> 01:40:34,740 It's going to take two. 1702 01:40:34,740 --> 01:40:37,770 Actually, it's going to take used variable and unused variable, 1703 01:40:37,770 --> 01:40:40,590 and it's just going to return that used variable. 1704 01:40:40,590 --> 01:40:42,675 So this would be considered poorly formatted. 1705 01:40:42,675 --> 01:40:44,550 Let's make it even worse by making it inline. 1706 01:40:44,550 --> 01:40:48,290 1707 01:40:48,290 --> 01:40:50,510 And so now let's run eslint on that. 1708 01:40:50,510 --> 01:40:55,384 1709 01:40:55,384 --> 01:40:57,050 And now it's actually going to complain. 1710 01:40:57,050 --> 01:41:00,790 It says, hey, unused variable is defined but is never used. 1711 01:41:00,790 --> 01:41:05,890 And so we happen to have a config that says, don't allow unused variables. 1712 01:41:05,890 --> 01:41:09,380 It's also saying, an unexpected block statement surrounding the arrow body. 1713 01:41:09,380 --> 01:41:12,450 Move the return value immediately after the arrow. 1714 01:41:12,450 --> 01:41:17,580 So, in other words, it knows that an arrow function can just implicitly 1715 01:41:17,580 --> 01:41:21,510 return, so I don't need to do this arrow function and then 1716 01:41:21,510 --> 01:41:22,660 a block that says return. 1717 01:41:22,660 --> 01:41:24,980 I can just return implicitly. 1718 01:41:24,980 --> 01:41:28,720 And, lastly, it says, replace return unused variable with a new line. 1719 01:41:28,720 --> 01:41:32,310 So it wants me to also add a new line here, like that. 1720 01:41:32,310 --> 01:41:34,840 1721 01:41:34,840 --> 01:41:38,730 So I could go ahead and fix all of those things manually, 1722 01:41:38,730 --> 01:41:41,740 but it turns out a couple of these can actually be fixed by Prettier. 1723 01:41:41,740 --> 01:41:47,810 So I can do npx eslint API, pass it that fix flag, 1724 01:41:47,810 --> 01:41:51,490 and it's going to automatically rewrite the file as much as it can. 1725 01:41:51,490 --> 01:41:55,590 And so it still has an error that unused variable is defined 1726 01:41:55,590 --> 01:41:59,610 and not used, because it can't really rewrite that part of my code. 1727 01:41:59,610 --> 01:42:02,490 It doesn't necessarily know if that unused variable is also 1728 01:42:02,490 --> 01:42:06,927 modifying some data structure that is necessary for the logic. 1729 01:42:06,927 --> 01:42:09,010 But it lets me know that, hey, you're not using it 1730 01:42:09,010 --> 01:42:12,760 so maybe there's a possible bug here. 1731 01:42:12,760 --> 01:42:16,020 So let's check out what it did. 1732 01:42:16,020 --> 01:42:19,660 Well, it removed that return automatically for us. 1733 01:42:19,660 --> 01:42:25,809 And so now let me fix that unused variable. 1734 01:42:25,809 --> 01:42:26,850 And now see what happens. 1735 01:42:26,850 --> 01:42:31,570 1736 01:42:31,570 --> 01:42:36,150 It's still going to complain because I have unused variable with parentheses, 1737 01:42:36,150 --> 01:42:41,150 and our opinionated code style guide says, hey, there's only one argument. 1738 01:42:41,150 --> 01:42:42,840 You should drop the parentheses. 1739 01:42:42,840 --> 01:42:46,870 It turns out this one is also potentially fixable, 1740 01:42:46,870 --> 01:42:53,370 so I can do npx eslint this file, and we'll pass the fix flag. 1741 01:42:53,370 --> 01:42:55,200 And lo and behold, no more errors and. 1742 01:42:55,200 --> 01:43:02,382 I can check the API and see that it did remove those parentheses for me. 1743 01:43:02,382 --> 01:43:03,090 So this is great. 1744 01:43:03,090 --> 01:43:07,980 It's just a little bit tedious to manually lint every single file 1745 01:43:07,980 --> 01:43:09,030 that I want. 1746 01:43:09,030 --> 01:43:14,820 So it turns out that we can run eslint as an npm script. 1747 01:43:14,820 --> 01:43:21,570 And so, just like our authserver has a script that 1748 01:43:21,570 --> 01:43:28,640 allows us to do this thing called npm start and start the server here, 1749 01:43:28,640 --> 01:43:34,070 the reason that it works is because, in our package.json, 1750 01:43:34,070 --> 01:43:35,960 we have this thing called scripts. 1751 01:43:35,960 --> 01:43:39,870 And we have defined a script called start that just runs that index file. 1752 01:43:39,870 --> 01:43:45,260 And so we can also, in this project, add a script 1753 01:43:45,260 --> 01:43:51,710 to our package.json to lint our file. 1754 01:43:51,710 --> 01:43:54,760 And so let's go up here, add this thing called scripts. 1755 01:43:54,760 --> 01:43:59,000 1756 01:43:59,000 --> 01:44:01,812 And now I'm going to add a script called lint. 1757 01:44:01,812 --> 01:44:03,170 And what's it going to do? 1758 01:44:03,170 --> 01:44:05,450 Well, it's going to run eslint. 1759 01:44:05,450 --> 01:44:09,740 And I don't have to do ./node_modules/.bin/eslint 1760 01:44:09,740 --> 01:44:14,150 because the package.json knows that, for this particular project, 1761 01:44:14,150 --> 01:44:20,090 it knows that eslint is installed as a dev dependency for this particular 1762 01:44:20,090 --> 01:44:20,590 project. 1763 01:44:20,590 --> 01:44:24,720 So it knows that it should look for that in that ./node_modules/,bin/eslint 1764 01:44:24,720 --> 01:44:25,920 file. 1765 01:44:25,920 --> 01:44:27,757 So we can just use it here. 1766 01:44:27,757 --> 01:44:30,340 And then we can just start listing off any files that we want. 1767 01:44:30,340 --> 01:44:32,250 So we can do api.js. 1768 01:44:32,250 --> 01:44:40,290 We can do our components directory, and list off any other ones that we want. 1769 01:44:40,290 --> 01:44:43,510 And for this example let's just do those two. 1770 01:44:43,510 --> 01:44:45,300 And now I can actually just run that here. 1771 01:44:45,300 --> 01:44:48,870 I can do npm run to see exactly what scripts are available. 1772 01:44:48,870 --> 01:44:50,310 I see that we have a lint one. 1773 01:44:50,310 --> 01:44:53,480 So now I can run npm run lint. 1774 01:44:53,480 --> 01:44:59,561 And it will go ahead, run this eslint api.js and /components for us, 1775 01:44:59,561 --> 01:45:01,560 and it will go ahead and just lint all of those. 1776 01:45:01,560 --> 01:45:04,590 I just noticed there's no such thing as components. 1777 01:45:04,590 --> 01:45:08,440 So maybe we should run that as our screens page instead. 1778 01:45:08,440 --> 01:45:14,580 1779 01:45:14,580 --> 01:45:18,000 And now this will automatically lint all of the files that we want for us. 1780 01:45:18,000 --> 01:45:26,820 And, as we see, I had quite a few lint errors, including navigation 1781 01:45:26,820 --> 01:45:30,790 is missing in props validation, because our style guide says, 1782 01:45:30,790 --> 01:45:34,230 hey, if you're going to use a prop, make sure to explicitly state 1783 01:45:34,230 --> 01:45:35,400 those prop types. 1784 01:45:35,400 --> 01:45:38,400 And so you're welcome to use this config file if you want. 1785 01:45:38,400 --> 01:45:42,090 You're welcome to use Airbnb's JavaScript. 1786 01:45:42,090 --> 01:45:43,530 There are many others online. 1787 01:45:43,530 --> 01:45:48,300 But now you should have no problem styling your project because now 1788 01:45:48,300 --> 01:45:51,750 you have something to yell at you and fix it for you. 1789 01:45:51,750 --> 01:45:55,970 So thanks, and next week we'll start looking at performance. 1790 01:45:55,970 --> 01:45:57,297