Redux Middleware/Async/Thunk: The complete Guide

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
in this video I want to talk about redox middleware now the purpose of redox middleware is to intercept an action before it reaches the reducer so when a user interacts with their application and let's say they click a button that's going to trigger the UI to dispatch an action and that we already understand however instead of sending that action directly to the reducer like we normally would we're going to send it to the middleware and that's why it's called middleware right because it sits in between the dispatching of the action and the reducer and so once the middleware it gets that action object it really has the freedom to do anything it wants so it can perform any extra logic it can modify the action object itself so if it so if you wanted to what we could do is we could actually change the action type we can change the payload we can add extra payload we can remove the payload heck we can even drop the action object so it never makes it to the reducer so we've got a lot of flexibility with what we can do with middleware the only thing you can't do is change the state itself and that's obvious because the reducer is ultimately responsible for you know managing and changing the state so the middleware can't do that but everything else any other kind of logic you want to perform the middleware is good to go and if you're having a hard time trying to visualize where all of these pieces fit together on the screen I have a diagram that I've shown you before in our intro to redux section however there's one minor modification and I've added the middleware in there so you can see the middleware right there it's going to be sitting in between that action object and the reducer so when the UI dispatches the action right it's going to send that action object to the reducer however in this case the middleware is going to catch it right and so our codes gonna run in the middleware and it's going to perform whatever actions we want and then once we're done with that we can then send it to the reducer who can then figure out what the new state is and the great part about middleware is that you know we don't necessarily just have to have one middle where we can actually string a bunch of middleware together so here in this example I've got three different middleware and the way that it works is that action objects gonna get sent to the first middleware the first middleware is going to perform whatever actions it needs to or whatever logic and once it's done it's going to send it to the second middleware and once that second middleware is done send it to the third Middleware and whoever is the last middleware in the stack which in this case the third middleware he's going to then send it to the reducer so we have the ability to add a whole bunch of extra functionality by adding in extra middleware into our stack so we have to discuss what exactly is the middleware because I'm sure you're thinking like you know what is it you know is it a special react or redux object or anything like that no middleware is really just a function that returns another function and you'll see how simple it is once we actually get to coding it which we're actually going to do in a few seconds so I think that's a good stopping point for now we'll stop here and we'll get started on building a very simple application that's going to be used to demonstrate how we can use middleware to add extra functionality okay to get started we're going to create a new app I'm just gonna call this Redux middleware all right so once that's complete we'll just change into that new directory and this could be called Redux middleware and I'm gonna open that up in BS code now unfortunately for us guys for me to actually demonstrate how redox middleware works we need to have a fully functional Redux application so I'm gonna have to build out you know a simple Redux application and we know that even simple Redux applications have a lot of boilerplate so just bear with me I'm gonna build out a simple counter app that uses Redux and once we've got that built out and confirm that it works we're gonna then start implementing some Redux middleware and so like we normally do I'm going to delete all our files it will define our index J s file and we're going to import react and import react router Dom also need our app JSX file you create a blank component you you you and then here we'll just render out our app component so we'll import it well let BS code import that for us and then finally we'll do a document dot get element by ID and that to route all right and so we're gonna open up our terminal and there's a few things that we have to install so the first thing is going to be NPM install we need Redux as well as react - redux all right once that's complete we'll just start our application just to make sure that everything is working so far and it looks like we got an error I'm guessing I just forgot to hit save all yep there we go and if I go to our console and we should see no errors perfect and so what we want to do in our app component is I'm gonna build a simple counter so we'll just put in an h1 tag and beneath the h1 tag we'll need two buttons and the first one is going to be for increment to will make the first one decrement and the second one is going to be or increment and now we need to start wiring up the Redux portion of our application and so I'm not gonna put everything into their separate folders because this is just too small of an application and I want to make this as quick as possible so what I'll do is I'll put an e I'll create a new file for reducer ojs and then I'll also make a new one for action AS and so for our reducer well we want to make sure that we're going to export it so we'll do export Const and we'll call this counter reducer our producer is going to take in two properties state as well as our action and we know that we need to give it a default value so our States just going to be a simple number and so I'm going to start it out a zero and then we have a switch statement and we're gonna switch on action type and for the first case this is going to be when action dot type equals increment and so here all we're gonna do is just return a state plus one and then the second case is going to be for decrement and this is just going to return a state - one and then lastly we need our default case which is going to be returned today right so our reducer is done the next thing we need to do is define our actions so we're going to create a few action creators when do you Const increment and this is just going to return you a type of increment and we'll just copy that and do the same thing for decrement you all right and we want to make sure that we're exporting both of these and then finally let's go to our index jsn define our store so we'll do a Const store equals and then we have to import create store from the redux library so we'll let vs code do that for us and then we'll pass in our reducer so we have to import that as well and I reducer was called I think counter reducer yep well let us go to import that as well and let's just do a save all actually let's go ahead and wire up the rest of our application so we know that first of all our state we need to actually retrieve our state so that we can render it to this h1 tag and so we need to use the use selector hook so we'll do const i'm just gonna call this count I will just say equals use selector and so we'll import that from the Redux library and we'll say taking the state and we want to return all of the stake as the state's just a number so we can't really slice that up any further and we'll render out count then we also want to dispatch a few actions here so we'll do const dispatch and we'll set that equal to use dispatch on-click event handlers on the button so we'll do on click then they'll just be an arrow function I will say a dispatch so now we need to dispatch the action so we want to import increment no the first button is for decrement so we want to import decrement so we'll do that with BS code and remember don't forget the parentheses because we want the function to actually run I'm just gonna copy this and paste this down under the increment button and just update that to increment so increment and import that perfect and so I think our app should be actually done so let's just quickly test this out I'm sure there's something I forgot and yet clearly up yep so I forgot to actually create the provider so we want to wrap our entire app between provider components and so we'll do that here and so I'll just say provider and we want to import that from the redux the reactor Redux library and we'll just take this app component and jam it right in there passing our store soft or equals store right because we called its store all right so let's test out our application so if I hit increment it goes up perfect and then if I go to decrement great so our application is done from a Redux perspective and so now it's time to actually make our first middleware okay and you know as I stated before our middleware is just nothing more than a function that returns another function and so you know we could define our middleware in a new file that's in a different directory but I'm just too lazy to do that so I'm just going to write it out on our index j s5 so that we can just see everything all on one page it just makes things a little bit easier and so I'm gonna define our first middleware in this middle way is just going to do nothing but log a few things to the console like log o you've dispatched an action it's something very simple and so I'm gonna call this a middleware call my logger it's just there to log and so basically we have to pass in our store so we have access to our state and that's important because you know we may need to access the the values in our state to perform certain calculations and things of that nature so we do have access to that so we pass that in there and this function is going to like I said return another function so we we're gonna call return and then we're gonna return a new function and within this new function we get access to the next function and I'm going to explain that in a second so just bear with me and this function is going to return another function I know it's through its is triple functions its inception of functions but bear with me guys it'll all make sense in a second and then finally this function the the the second function is going to return one last function so the last function gets access to the action object which got dispatched obviously and we're going to then perform whatever task we wanted so within this final third function we can now perform whatever the hell we want and like I said we're just going to console dot log I'm just gonna say middleware Rann okay so this just is gonna confirm that you know our middleware actually did something in red okay so this is our middleware I'm going to save our application and let's test this out and see if it works all right so I've hit increment I hit decrement nothing happens and I'm sure you guys know why already we haven't wired up our middleware anywhere in our code so we haven't told redux to actually use it and so the way to actually use or define our middleware is within our create store you know we're passing in our reducer as a first argument however in our second argument we can pass in the apply middleware and so this comes from the Redux library so you can see I've just imported that from the Redux library and then within this applied middleware we have to pass in all of our middleware and so our middleware at this point in time is just my lager so we'll pass in my lager okay so that's all you have to do to wire up any middleware and if you had more than one middleware you would just do a comma and then add in you know second middleware and then if you want a third one we can do third middleware and so on you all right so we've saved that and now let's test this out so if I hit increment look at that it says middleware ran so this confirms that our middleware has been wired up and it's functioning properly you know when I was going over the idea behind middleware you know I did want to emphasize that the whole principle behind the middleware is that it catches the action before it hits the reducer and that's important to understand and I don't want you guys to just take my word for it I want you guys to actually see and verify that yourself and so we can do that by going to our reducer and I'm just going to console dot log something in our reducer just to confirm when our reducer actually runs so we'll just say yay our reducer ran so we can take a look at these timestamps or these console dot logs to see you know when each one of these things actually runs okay so I've saved our application and it's reloaded and you'll see that it already ran that yeah reducer ran which is within our reducer and that's because when we start our application our reducer will run once to create the initial state of our application but now ignoring this moving forward let's hit the increment button and let's see what happens so when I do that you'll see that well it says our middleware ran great but it didn't say that our reducer ran and it's odd that our number didn't increase so we've actually broken our application by adding this middleware so it looks like our middleware is receiving the action object it's performing some action which is printing out middleware ran but then it's just dropping the action object well it's actually doing that because we didn't tell hit to actually pass the action object to the reducer and we have to explicitly tell all of our middleware to do that and the way we do that is we do a whoops we do a return and then we call the next function and we pass in our action so the next function all it does is just tell it to pass our action it just tells it to you know pass our action object to the next middleware or to the reducer if it's the last middleware in the stack so if you don't have that next function defined what's gonna happen is that it's just gonna the action object is gonna die on our middleware which is what's happening now so we have access to that next function we call it and then we pass in our action object which we have access to in that third function as an input argument so we're just saying you know we received an action object we're gonna consult out log something and then we want to take that same action object and send it to the next middleware or to the reducer if we're the last middleware in the stack but let's hit save and let's test out if this worked so let's hit increment looking at our application it looks like it's working and now I want you to notice the order in which we've printed out things so you'll see that remember this is the first one that runs when our application first starts up so we're gonna ignore that however after we hit increment you'll see that our middleware ran and then our reducer ran so this confirms that you know one our middleware takes place before our reducer so our middleware will receive the action object it'll run whatever code then I'll pass that action object and send it towards the reducer who can then run and then update our state now in if you look at our middleware it's a little messy and what you'll actually see out in the wild is that we'll actually shorten all of this syntax a little bit to make it look a little cleaner and so instead of defining our middleware like this I'm gonna just comment that out I'm gonna show you these shorthand syntax and so this isn't instead of something specific to redux or anything this is just basic shorthand notation for arrow functions so instead we can actually define like this so we'll do Const my lager equals passing the store into the first function and then we returned the next function the second function and in the second function we pass in the next function and he's going to return another function where we pass in an argument of our action object and then finally we have all the code that's going to run and so that's just going to be these two lines right here but we'll do the same thing we'll do console dot log and then we'll say middleware ran and then we want to return and remember always call it the next function so that we can actually update our state and so let's test if this works so if we hit increment great and we can see our middleware ran and then I reducer ran so our applications working great and we've got a middleware in place that's just doing something very simple which is just printing something out to the screen alright so the next thing I want to do is I want to add in a second middleware because why not write I want you guys to know that we can put in as many middleware as we want I'm going to create a new one I'm just gonna actually copy this is a lot to type out I'm just gonna call this what do you think second middleware and I'm just gonna say second middleware ran we want to make sure we wire that up so we'll just do a comma second middleware alright and so the order matters right when we put my lager first that means that's the first middleware that's going to receive the action object and then the second one is going to be the second middleware so the order is important to keep that in mind and let's save that we'll test this out again and you'll see that we've got the middleware ran which is our first middleware and then we've got our second middleware ran okay so now just to make things a little bit more fun let's go ahead and add a third middleware but this middleware we're gonna have we're gonna make it do something a little more interesting than just printing it out into the console so I'm gonna do actually I'm just gonna copy this cuz I don't want to type that out again and I'm gonna call this um the good name is going to be cap at 10 you might be wondering what that means well I want to set up a awesome middleware that's basically going to look at our application and it sees that once the counter reaches 10 I don't want it to increase further instead what I wanted to do is if I hit increment once it's already at 10 I wanted to actually decremented by one stupid application but I think it's a little cool little demo to show you what middlewares are actually capable of um let's delete this let's delete all of our code for now and let's see how we can tackle that so we the first thing we obviously need to do is we need to be able to get the value out of our state and if you notice in our first function and in our outer function we're actually passing in the store so let's do a console dot log and let's just see what our store is and what we can do with it make sure we wire that up so there's gonna be a third middleware so let's cap at ten it's safe and let's hit increment obviously this isn't gonna increment because within our third our third middle where we're not calling the next function so we're actually breaking our application but let's take a look at the console dot log and you'll see that the store has access to two functions right it's got the dispatch in the gate state so we want the get state this is gonna allow us to actually get our state obviously and so what I'm gonna do is I'm just gonna set up in a simple if statement and we'll say if stored get state we'll say if that is greater than or equal to lips greater than or equal to ten is where's gonna get really cool we have access to our action object right and so that's that action object that's defined here for like increment or decrement and we can do whatever we want with it so I can actually modify its properties I can I can send a new action object so as an example what I'm going to do is instead of just doing a return next action which would mean that when or when our value or state is greater than 10 we're just we're not going to do anything we're just going to send our original action object to the next middleware or to the reducer instead what I'm gonna do is I'm gonna create a new action object and I'm gonna say type is going to be set to decrement so when we're at so when when we dispatch an action and the state is already at 10 or higher no matter what action object we send whether it's an increment or decrement I'm going to always send a decrement so that we never go past 10 okay and then obviously you know when it's not greater than ten we still need to do a next and send our original action object so that we can actually increment or decrement it like we normally would so let's test this out so let's hit increment that's working if I hit it again still working let's make sure decrement is still working as well okay so everything's good so now let's go up to ten and actually before I do that what I want to do is I want to go to I want to remove all of our consoled out logs because it's just getting a little cluttered you and I want to go to our reducer and here I want to print out something I'll say our action type is and we're just gonna return our action that type and so I'm gonna do a save all and now let's test this out let's just make sure I didn't break anything okay and so now I'm gonna go up to ten so I meant nine hit increment again and notice our console logs you can see that actually let me go down just to make sure that you guys following so you can see our last action that we sent was a decrement if I hit increment our last one was an increment if we hit it again we've got two increments we've got three we got four we got five now when I hit increment again notice what happened there all of a sudden even though I hit increment our action that type was a decrement and that's because of that middleware we just configured so this middleware said you know our state is greater than or equal to 10 so what I'm gonna do is I'm gonna call the next function so I'm gonna send it to the reducer with this new action object and so that just shows you the versatility that we get when we have middleware you know we can change our action objects we can add in extra payload we can make asynchronous calls API calls within our within our middleware to perform these tasks and so hopefully you guys have a solid understanding of middleware it's actually an important concept I know that I've been just doing boring examples of middleware with this this logging but you know regardless of what you do with your middleware it's the same exact concept behind all of them and there's actually a lot of third-party middleware that we can use and I'm actually gonna show you one right now real quick it's also a logging middleware which I know is boring but it's a very simple example so let me pull up the NPM Doc's real quick and so if you go to NPM and just search Redux logger this is gonna be the first result you get and so this is just a really nice and fancy logger for a Redux application and we can actually install it with an NPM Ivy ducks - logger I'm gonna open up a new terminal I'm gonna do NPM I redux - logger and once that's done we're gonna just wire that up so and so the first thing we have to do is we need to import it so we'll do import logger from oops net form from Ram redux - blogger and I'm going to make this our fourth middleware so we'll do redux no sorry we need lager we'll hit save and we're gonna take a look at this amazing middleware so increment this is the lager kicking in right here the new third party lager and so you can see that it's a it's a it's like a fancier console dot log right so it's got our action object it can it'll show us our original state before the action object was sent it'll show us the actual action object as well as what the next state was after the reducer ran and so this is actually a cool little logging middle way that you might even want to use on all of your application it just makes it a little easier to kind of see what's going on and that way you don't have to console dialogue everything yourself although you know in all honesty with the redux dev tools this probably isn't as helpful I'd rather just use that but just this is something to keep in mind and you know if you ever want to use any third-party middleware just keep that in mind it's as simple as importing it and then just adding it onto the middleware chain in this section I'm going to introduce you guys on how to incorporate asynchronous actions like fetching data from an API within a Redux application and what we're gonna do is we're going to make use of the JSON placeholder API it's an API that we've used throughout this course and we're basically just going to fetch a bunch of posts and render it out into the screen but the reason why I want to do this very simple project with Redux is that I want to show you guys how and why asynchronous actions actually break redux applications and it's important for you to understand why it's happening so that when I actually give you the workaround you'll understand why that workaround actually works and we're actually going to be able to perform asynchronous actions within a Redux application by making use of middleware and that was also one of the reasons why I wanted to make sure that we covered the middleware section before we started working on the asynchronous section because if you guys don't have a strong understanding of middleware you're gonna be confused as to what exactly we're doing and so in the next video we're gonna start coding out our application and we'll start out with the basic redux of boilerplate all right so that's enough talking I'll see you guys in the next video alright guys so I've created a new project I just call it it redux async and we're gonna do what we usually do with most of our projects we're going to delete all of our files and start everything from scratch and there's going to be two files that we want to create there's going to be our index J s as well as our apt JSX and within our app JSX I'm just going to create a basic component and we're going to render out an empty div and our index dot JSP act as well as react router Dom you you you and before we start our application there's a few packages that we have to install and so we'll do an NPM install Redux react - Redux and we'll also need Axios for fetching data from the API that's complete we can start our app and I'm just gonna open up our console just to verify that we don't have any errors and it looks like we don't and so now let's go about creating our components for our new app and we're just going to create one component and I'm going to call this the Post's component and this component is going to be responsible for fetching the data from the API as well as rendering out that data and for now I'm just going to create the the base config for this component but before we really do anything else with this component I want to start getting all the Redux set up and so we'll start off by creating our reducer you and this reducer just like every other reducer is going to get access to the state as well as the action and for now I don't want to actually create the logic for a reducer so I'm just gonna return the state so this is basically just a dummy reducer just so that Redux won't actually throw any errors when we try to do anything else and let's give it an init state so if I do Const in its state and let's figure out what we want our state to look like so if we go to our JSON placeholder API and just like the post endpoint you'll see that it returns a list of posts so I think in an array would be a good option for us so the default state is going to be an empty array you and we want to we wanted a default or sorry we want to export our reducer and in our index is let's wire up our store so we'll do cons store and we want to import create store on the redux library and let's pass in our post reducer and don't forget to import that as well that we need to wrap our entire app with the provider component and we'll pass in our store and so for now let's do a save all let's just make sure that we haven't broken anything so no air so I think we're looking good now let's figure out how we can actually fetch data from our post component and you know if we weren't working with a Redux application I think what we would normally do is since we want the post component to fetch the data when the component mounts that means that we want to use the use effect oh right so anytime you want to do anything when the component mounts you're gonna have to use the use affect hook so let's import that in function and so normally well actually since we wanted to do it only when it mounts we have to pass in an empty dependency array but normally at this point what we would do is we would call Axios and do an Axios get to whatever URL to reach that API however with Redux is going to be a little different we don't want the component to actually be performing the API call the API request instead what we want to do is we want the action creators to actually handle that responsibility so here we're going to delete this and we're actually going to create our actions now and we're just going to have one action and I think that's going to be called a we'll call it fetch posts and our action object that we returned from our action creator you know sorry our action reader has to return an action object it cannot return anything else so we have to do return and then an action object just has to have one property which is the type and that's going to be fetch posts and we'll set up the equivalent switch our case statement in our reducer in a bit but we'll we'll handle the API call first before we actually handle the all the reducer logic so at this point our action Peters doing what a normal action creator to do which is just returning an action object however like I said we want the action creator to actually handle the logic of fetching the data so what we're gonna do is we're going to first import Axios so I'm going to do Axios we'll let that import for us and then we want Axios get and we have to figure out the URL that we wanted to go to so you can just copy this URL directly want to store this result so we'll do constant response equals Axios get and since this is a so since this API call it takes a certain amount of time we're going to use the async/await syntax and what we want to do is we want to take this response and we'll do a little just add a extra payload property and we'll have been the will embed the response on there and we actually want response to that data I already know what its gonna look like but uh response will give us some extra information that we don't care about we just care about the lists of posts and that's what response that data is gonna be so if we go so just to recap what we did with in this action creator this action creator will fetch the data from Axios it'll store it in this response variable and then it's gonna return an action object which has a type of fetch post as well as a payload property that's going to actually have the lists of posts so I think everything looks good now we have to go back to our post component and we want to actually dispatch that action so we have to use the use dispatch hook so we'll do const dispatch equals use dispatch and now we can then do a dispatch and we'll dispatch what is it called fetch whoops fetch posts so we'll import that and don't forget the parentheses because we actually want to run that function so you know I think we're good to go if we do a save all let's take a look at what happens and I realized I forgot to do one thing we're actually not rendering out our post component so let's put that in our app component here we'll just put it right between those two divs and now let's do a save all and let's take a look at what's happened and it looks like we've aired out and I think it's important to actually take a look at the area it's saying that actions must be plain objects use custom middleware for async actions so this is expected I actually you know kind of forewarned you guys in the previous video that by default you know Redux if you try to perform any asynchronous actions it's going to throw an error and I want to explain to you guys exactly why that is okay guys so when we start to perform asynchronous actions within redux there's two reasons why our fetch posts action is not working the first reason is that like I said before action creators can only return plain JavaScript objects that have a type property so we can only return an action object if it tries to return anything else we're gonna get an error from react and redux the second issue is that you know when we go to fetch data from an API the action object will get sent to the reducer well before the data can be fetched from the API server right because you know when we dispatch an action it's you know within nanoseconds that it gets sent to the reducer like it's an instantaneous whereas sending an API request to a remote server could take up to a half a second or more and so that API request will never get completed in time before the action gets sent to the reducer and so the reducer will never have access to the posts that we're trying to retrieve now the error that we saw just now is due to the first item on this list it's because we're returning something that isn't an action object so let's go back to our code and take a look at what exactly is happening and if we go to our action object and we see what what exactly we're turning let's see why we're getting an error and so here it says you know we're returning an object okay so so far everything looks good and it has a type property so you're probably thinking well you know what are we doing wrong we're returning an action object it should not be throwing out this error saying that we're not returning an action object well here's the thing because we're using this async/await syntax there's a lot of funny business that's kind of happening behind the scenes and to actually explain what's happening I want you to keep in mind that this is not the code that's going to actually run on the web browsers when we go to deploy our application instead what's gonna happen is using a tool called Babel our codes gonna get transpiled into a code that all web browsers can understand so we can go to Babel calm and let me pull that up for you guys right here so this is bable bable jest io / repl and you want to make sure you select all of these tabs on the left and what I want to do is I want to just copy this real quick and I'm gonna paste it into here and what this is going to show us is what babel is going to convert this code into so when we go to actually deploy this app this code is going to get turned into this mess of code and so you can see that because we have the async/await syntax we have all of this extra code and if we go into this fetch post function right here we can kind of see why we're getting that error now and there's two main cases that I want to focus on you can see that there's a switch statement and we've got case 0 in case 2 if we take a look at case 2 this looks like kind of what we want right it's returning an object with a type of fetch posts and the payload of response data so it looks like if it hits case 2 we're returning an action object like we expect to however if we go to case 0 it's returning Axios get so in that case we are not returning an action object and that's why we're getting that error so this is all kind of happening because we're using that async/await syntax and you're probably wondering well you know what happens if we just remove it well if we remove a wait and we remove async you can see that well this gets converted into exactly what we want and we're returning just a simple action object okay so you might be thinking well that's a little bit inconvenient but you know we can go ahead and just remove that and everything should work so let's let's take a look at what happens when we actually try to do that in our code and so now we're actually returning a response or sorry a promise so I'm going to change that to promise and I'm going to change this to a promise as well and let's hit save and let's load that and let's see if we get any airs and it looks like we don't get any errors and so it looks like it's working however I'm gonna tell you guys right now that this is not going to work so even though we don't get errors you'll see that when we actually go to implement the rest of our code our application is not going to work in a way that we expect it to and the reason for that is because of this second line right here so this is the second issue that we run into when we try to perform asynchronous actions within JavaScript because what's going to happen is we're going to try to fetch the data oh and that's going to return a promise however when we go to actually dispatch our action that action will get sent to the reducer well before we resolve that promise or well before we get all the posts back from the API and so if we go back to our code here you'll see that we get a promise so right now that promise doesn't actually have all of the posts that we're trying to fetch it's just a promise and so we'll return this object which gets sent straight to the reducer and we're still waiting on the API to actually we're still waiting on this JSON placeholder API to actually send the data that we want however since the action objects are already at the reducer the reducer is then going to perform whatever logic it needs to but it's gonna do it without the actual post that we were trying to get so our app isn't going to work when we try to do that and so that's really the issues that I wanted to discuss when trying to perform asynchronous actions within JavaScript oh sorry within redux in the next video I'm gonna show you guys how we can actually get around this issue and actually perform an asynchronous action within redux okay guys so to actually get this to work what we have to do is make use of a third-party middleware that's right we're gonna use middleware to solve this issue and there's a middleware called redux thunk that's going to fix this issue so we do an NPM install redux - thunk and let's wire up this middleware and so if you guys remember in the previous video to actually do that we have to import apply middleware and we also have to import in thunk so we'll do import bunk from redux - sup okay so now let's talk about what Redux thunk does and you're gonna be you're gonna have your minds absolutely blown when you find out what it actually does redux thunk does one thing for us and that is it allows our action creators to return an object or return a function so that's the only thing that it's changing is that now from our action creator right from our fetch post action creator instead of returning an object we can actually return a function and you might be thinking well you know how does that help us in this situation well let's actually go ahead and implement a second function within this action creator and I'll show you guys how that actually helps us I'm just gonna comment this out and we're gonna define a new action creator we'll do export on stretch posts again you you and so now because we have Redux thunk we can return a function so I'm going to return a new function and we can pass in two properties we get the dispatch function and we get the get state and you guys should already be familiar with the get state this just allows us to access what's in our state but within this inner function we can now perform whatever we want to so we're going to first of all we're going to perform that API call from with axial so do constant response equals oh wait dot oh excuse dot yet and then we'll just copy this link you and we're gonna make this an asynchronous function now you might be thinking well you know are we gonna just run into the same issue right we're doing async/await but we should hit that same limitation where we're returning something that's not an object or even a function and that is true however Redux and react only cares about what the action creator returns which is the outer function it does not care about what gets returned from this inner function because at the end of the day the outer function just returns the inner function we don't care about what the inner function returns so we can use async await so we can use the async await syntax without any issues and now this is where the real magic happens is that we are basically kind of almost pausing our action from getting dispatched so since we dispatch the action we're kind of saying we need to wait till we get a response back from the API endpoint and only once we get the response back should we then continue dispatching this action and so here the thing that we need to do now is we need to actually dispatch the action manually because we kind of pause the app so to do that we do dispatch right we have access to the dispatch function and then we dispatch and then now we have to pass in the action object we want to dispatch so we'll just define a new object and this is going to have a type of fetch posts right I'm just copying the same name that we had before give it the payload because since we have the await syntax this will resolve and so now we can pass that in and save response dot data right so hopefully you guys understood that but just to make it clear let me just reiterate what's happening since we're returning a separate function it allows us to use the async/await syntax and because we have Redux middleware we can return a function instead of just an action object and so within this inner function we are going to send an API call to this endpoint we're going to wait for us to get a response and only once we get a response will we then manually dispatch our action object and so I think this should work if we do a file save all now let's go back to our react app we get no errors but that's because I've stopped our development server and it looks like we got an air so stored get state it's not a function so let's see where this broke alright guys so I see where the issue is I forgot to actually apply the thunk middleware into our apply middleware so remember within the apply middleware function we have to actually specify all of the middleware that we want to use so I forgot to do that and I think it's basically kind of giving us an error saying you know how are you passing in the get state when you're not using thunks so let's just save that and now you can see that we have no errors and if I open up the console no errors so everything is working as planned and it looks like this fixed our issue however there's one thing I want to do I'm gonna do a console dot log and then we'll just do a response dot data just to make sure that we're getting our data so it looks like we are and so now since we have our data we can actually go into our reducer and apply the logic that we need to and so we're going to create our usual switch statement and we're going to switch based on action that type and we'll say for the first and only case when it's fetch underscore posts we're going to return action dot payload all right so what that means is we're just going to take this entire array and return it so that's going to be our new state and we want to make sure we give it a default and we'll just say return state save that and now it looks like we've probably got our Redux set up correctly however we're not actually rendering out anything onto the screen so let's do that just to make sure our app is working and so this component needs access to our state so we have to use the used selector hook so I'll do Const and I'll say posts equals use selector and what we have to do is we have to take state pass it in to this arrow function and we're going to return just state and within this div I'm just going to do a post dot map and we'll say as we iterate over each element we're going to return and let's just quickly see what our data looks like again and so just for simplicity sake I'm just gonna render out the titles of each of these things so we'll say return in h3 tag and we want to render out Al dot title let's hit save and look at that guy's so we've now rendered out all of the posts that we retrieved through redux okay so hopefully that made sense I know it may be a little confusing I don't think I did a great job explaining it but you may just need to do this a couple times before you really start to get the hang of it just keep in mind you will need redux thunk to be able to perform that little trick of being able to return this function so that we can then manually dispatch our action and not have that error where we're returning something that's not an action object and there's one last thing I wanted to show you and that's that we can kind of shorten this syntax a little bit because if you see this outer function right here you'll see that we're just returning this dysfunction right and so we can actually shorten that by removing the outer early braces to the outer function and then removing the return statement and then just bringing that async line back up to there and so this should do the same exact thing and you'll see we get one error but that's just because we're not passing in a key prop into our list but who cares about that and so this is the completed code and so moving forward I just you know make sure you use redux thunk and make sure that you always make your API calls and your action creators instead of your components now there's one last thing we need to address in our application and that is loading an error state and these were two things that we were able to address in our previous projects whenever we were trying to fetch data from an API we would also define two extra states one for loading and one for error and that way we could just update the UI and give the user a little bit of feedback you know to let them know that we're either actively loading and fetching data or if we have you know experience some kind of error in retrieving that data and with the Redux application we have to do the same thing except once again we're not going to be using local state instead we're going to be using the state within redux and the way we actually make use of this is by employing a specific pattern called the request success and failure pattern and with that pattern what we're gonna do is we're actually gonna define three different actions because right now we just have one action which is fetch post but we now want to break it out into three different actions we're gonna have one called request one called success and one called failure failure and the request action is going to be very similar to the fetch posts action it's going to get dispatched when we want to actually fetch the data and then we have the success action so the success action will actually dispatch once we receive the data from the API endpoint and the name makes sense right you know we've successfully received the data so we want to update the state to let the state know that we have successfully received the data so we're gonna dispatch a success action and then we have a failure action to wrap things up and we're just going to dispatch that if for some reason we were unable to retrieve the data that we want or we experienced some kind of error of sort I and so we can dispatch that action in and update the state accordingly and you know this is going to involve a decent amount of change to how we define our state as well as our reducer and so I've included what the final reducer is going to look like once we make these changes and if you look at the innit state you're gonna see that our state is a little more complex it's no longer just a array instead we have three properties we have items and that's going to be the array of posts so that's just kind of like a generic name I could have called it posts but you know by calling it items we can kind of reuse this state across different parts of our application then we have a loading property and so you know floating is set to true that means we are actively fetching the data and then we have an error property and so you can see right now it's set to a default state of null however if we do experience some kind of error then whatever air that we do experience then we can update that property accordingly and if we just kind of scroll through the reducer you'll see that the case for when we send a request all we're doing is first of all we're gonna clear out any errors so if there was an error from the previous request we want to make sure we clear that out and then most importantly though we're actually set the loading to true that makes sense right because we are trying to load the posts if we take a look at the post success and in this state you know we'll only dispatch this action once we've received those posts so we're going to set the loading to false and when we dispatch the action we're also going to send it with the payload which is going to be all of our posts and we're going to set it to the items array and then finally we've got the fetch posts failure and this is going to get dispatched if we experience some kind of error and so because we have experienced an error we're going to set the loading to false because we're no longer trying to load the data and we're gonna set the error property to whatever error payload that we assigned to the action item and then finally we're this is kind of an optional step this kind of varies depending on how you want your application to actually operate but in this case what we're gonna do is we're gonna clean it clear out the items array because we were trying to load some new kind we were trying to load some new posts and we couldn't do it so you know we could keep the old post but I think it's a better user experience if we clear it out however keep in mind that you know depending on the specific application you may or may not want that behavior so guys it's not really that complicated it's just a little extra boilerplate to get all of this set up but we'll go ahead and implement that now and you guys will see that it's pretty straightforward but I actually want to start off by actually implementing redux dev tools so that we can actually see each one of these actions getting dispatched and the redux dev tools will just make it a little easier to kind of trace it out step by step so I'm gonna start off by well let me open up a new terminal and we'll do a NPM install Redux - dev tools - extension and we'll let that run and let's wire up the Redux dev tools so here we just want to import compose with dev tools you all right now this is where it gets a little different from when we wired up the Redux dev tools and I believe our miles app but since we have middleware what we actually have to do is take this and we want to take the compose with dev tools and we want to take our middleware and move it into the compose with dev tools so this is what the final store configuration should look like so we've got the reducer then we've got the compose with dev tools and then within there we've got the apply middleware with the with the list of all of the middleware that we want to run so let's do a save and at this point you should see the little green icon highlighted so we can just select that and let's just make sure that it's working so you can see that we sent a fetch post and we can see that our current state is an array of posts okay so everything seems to be good so far now let's now let's update our reducer with everything that we covered in that slide deck and so the first thing we need to do is change our default state we're gonna need in items property set to a empty array will set the loading property to false and the error property to null and we can delete this case we don't need that and we'll need three cases so we'll need underscore posts underscore requests and here we're gonna return our new state remember we always whenever our state is a object or an array we want to copy all the values in our state and we can do that using the spread operator and then we want to update each property manually they'll set the loading to true because when we first send that request we're going to be in a loading state and we want to make sure that we clear out any errors and I'm going to copy this and we're going to set up the other two cases and then this one is going to be a success and in this case the loading is going to be set to false to change the items property to be action dot payload and then our final case is going to be when we experience some kind of failure you and when we experience a failure we're obviously going to finish loading and we want to let the error to action and then most likely what I'll do is I'll I'll attach the error to the action object as to the error property so we can just grab it from action air I guess oh that's all the changes we need to make for our producer now let's go to our well first of all let's go to our posts our post component and I'm going to just comment this out because it's going to give us an error because we've made some changes and now you know because our state's more complex and we don't just have an array I'm gonna change this name we can just change this to state for now and so we're gonna grab all of the state in this case and let's move into our actions and this is where a lot of the changes are gonna take place too because we're gonna have to change our action creator because now a lot of the logic is gonna take place in there so I'm going to just copy this and I'm going to comment that out just for reference so now when we call the fetch posts action creator we have to think about what do we need to do right and so if we go back to our reducer we know that the first thing that we always want to do is dispatch the fetch post request action object so that we can tell the UI that we are in a loading State that's really all this is doing is that telling it it's in a loading State and we're gonna clear out any previous errors and so that's exactly what I'm gonna do I'm going to dispatch you it changes to a type of underscore posts underscore request alright so the first thing is done now after we fetch those posts it's now time to actually try to fetch the data from the API endpoint which we're already doing in this line however what I want to do now is actually wrap it in a try-catch statement and so if we hit an error we're gonna pass that error and it's gonna be called an error and so within this try-catch block basically we're going to move all of our code into the try block and if everything works great if we experience an error then we're gonna jump into the catch block so we want to take this part and just move that into the try block and once we receive that data we want to then dispatch another action and this time is going to be the success action because we have received all of the data that we wanted from the API so type is going to be said to fetch underscore posts underscore success and remember we need to actually give it the payload so the payload is going to be remember response data that's gonna be all of the posts and then finally we have to figure out what we want to do when we experience an error and so that's easy we just want to dispatch another action and this time the type is going to be fetch posts failure you and I'm going to assign our property to be the air that gets passed down into the catch block and so I'll just to air and remember whenever we have the error error we can just shorten this to just be error and we don't need this console dot log and we don't need this dispatch so that's all the changes we have to make to our action creator we will save all of that occasionally quick and hopefully there's no errors like that's good so no errors there and now let's go into our post component and let's actually render out our data the way we want to and let's implement a simple loading feature so I'm going to delete this keep that there for now so I can copy it but what we're gonna do is we're going to call will just say you render a posts you and here we'll just assign a simple if statement and say whenever the state we want to yes State sorry guys and we want to get the loading property from the state so we have to go to state dot loading so whenever we're in a loading state I'm just going to render out an h1 tag and say loading or else we'll just return what we were normally returning which is this you and remember we no longer have access to posts instead we have to do state dot items right so state and then dot items well hit save and look at that we're rendering out all of this data however I want to make sure that Redux is actually doing everything that we want to do so I want to make sure our loading functionality actually worked and you know instead of like trying to hit refresh and then trying to quickly see it we've got to read X dev tools for this and this is going to be super awesome and so if we look at the history of all of our state or all of the actions that got dispatched you can see that we sent a fetch post request and so if I click on this and go to state you can see what our state looked like after we sent that so we still had no data but loading was set to true and Eris was set to null right and then eventually we got the data we sent a fetch post success and at this point we've got our items array and loading is set to false and arrow error set to null and what's great about this is if we go to fetch post request I can then jump to it and it's gonna show our application in that exact state and then if I want to go to after we receive that data we can select jump there and it's gonna take us there so there you go guys that's all I wanted to show you guys when it comes to asynchronous code within redux it's not too bad there's a little bit more boilerplate that we have to kind of work with but you know you'll see that when you work with more complex applications it's nice to have this really neatly structured out
Info
Channel: Sanjeev Thiyagarajan
Views: 20,906
Rating: 4.9783783 out of 5
Keywords: react, reactjs, redux, thunk, async, asynchronous, middleware, fetch, data, api, programming, javascript, web, design, state, management
Id: qA6oyQQTJ3I
Channel Id: undefined
Length: 66min 3sec (3963 seconds)
Published: Wed Jun 10 2020
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.