Welcome back.
There's one final piece of the puzzle that we need with redux and something that we've been avoiding
up until this point.
You see if we go to App.js
You remember that we do this componentDidMount fetch request.
We have an asynchronous request.
We're making a request to an API grabbing their users and then finally setting that state to robots.
But how would that work in redux.
Because remember up until this point everything we've done has been synchronous because if we do the
same thing like we did with the search field it wouldn't work with fetching robots, because we're sending
an action of let's say fetching robots, but because that's asynchronous it's going to go off in another
world into the API.
And try and get the robots. In the meantime, the reducer is just going to check the robots and say there's
no robots yet because they are in API-land (and the request/action) hasn't returned yet.
Just like a promise.
So the reducer keeps going and doesn't make any changes because the state hasn't changed.
So how can we create actions that are asynchronous, that still go through the reducer the store and then
make changes.
And this is where we use, apply middleware again.
We can use something called redux-thunk to handle asynchronous actions like AJAX calls.
It's a middleware that provides a get state and dispatch functions that are passed on. You're able to
handle something called side effects like AJAX calls
with this package.
So we're going to show you how that works.
First thing we want to do is install the package redux-thunk.
So I'm going to say NPM install redux-thunk.
And by the way just like with anything there's tons of other ways to handle asynchronous action in
redux. It's just not built-in out of the gate so you need some packages.
There's other options but redux-thunk is the best and simplest to start off with.
All right.
We have a redux-thunk now and we can simply start using it by going to index.js
and within here we can say that we want to import something called a thunkMiddleware from redux-thunk.
Again you can read all about this, and there are documentation on Github. Thunk middleware,
we already know how to apply middleware to our redux app, right.
We have our applyMiddleware function.
We can simply add the applied or - not the applied - the thunkMiddleware.
And let me just put this on a new line so you can see it better.
thunkMiddleware to our applyMiddleware.
And this is ordered,
so it's going to go through the thunkMiddleware and then the logger middleware. Perfect.
So let's run NPM start.
And I'm also going to save this, all right looks like everything is running smoothly.
We have this middleware implemented now.
But how do we actually use it? Well let's start with the very first steps.
The first thing we want to do is create a constant so a constant for requesting our robots.
Remember in the app.js file we have this robots array that gets populated after componentDidMount
with, after we do the fetch call and we setState with the new users.
So in the constant we can do something similar to above.
We can export const REQUEST_ROBOTS. Now,
because this is going to be a promise.
It's going to have three states.
So the first one is going to be PENDING which is the very first time that we send that request it's
going to be pending we're waiting for the promise to return.
So we'll again use the same string here, and we're also going to have two states after the pending
returns which is SUCCESS
and also FAILED.
This request has three actions: pending, success, failed.
That we want to keep track of.
And again this is pretty much standard with all asynchronous actions like AJAX calls.
So let's save that, and the next step as we know is to create the actions using these things.
So let's go back to actions and this time instead of just importing CHANGE_SEARCH_FIELD let's import
a few more things.
I'm going to import, let's just copy and paste here because that's a lot of typing.
Let's go to Constants.
We're going to import pending
success and then finally failed.
let's save.
And when you get a warning that we're not being - we're not using them.
So let's create these actions now just like we did above we're going to export const and we'll name
this requestRobots and this action takes.
Hmm what's it going to take.
Well this is a little tricky but it's going to take the dispatch. And dispatch,
If you remember, and we go to App.js,
is something that we use in mapDispatchToProps that we get from redux to dispatch the actions to
the reducer.
And when we come back here, you'll see that we're going to actually pass this to our action.
So we have access to it.
But again for now just know that we're going to have dispatch, and within here we're going to do a
few things that's a little bit different than the above.
The very first thing when we request robots Well we want to dispatch the pending action so again we
can just do the standard syntax of dispatching an object that is of type request robot's pending
and the payload.
Well there's no real payload.
We just have a request pending, so we can just leave it the way it is right now without a payload.
Next we have something that is asynchronous.
We need to do a fetch call.
So we have our fetch function.
And if you remember we go to App.js
We have this API that we need to call, but you also need to do the response response.json all over
again.
So I'm just going to copy all of that, go to actions and just copy and paste this.
So we're fetching the users we're doing the .then and json, using that json method on the response.
And then finally in here we have two options.
We have the .then and .catch in case there's an error or something fails.
In the .then we receive some sort of data which will be users.
And once we've received that we can say dispatch, we're going to dispatch. What do you think we're going
to dispatch.
Well the object that is of type REQUEST_ROBOTS_SUCCESS
and this time the payload for this is going to be
the data that we receive from the API and with the catch very similar.
We'll get some sort of an error and this error is going to once again dispatch
and it will have a type of REQUEST_ROBOTS_FAILED
with a payload of let's give it the error that we receive.
All right.
Let me make this a little smaller so you can see. So we save that, and now we have our actions and this
action is a lot more complicated than the setSearchField.
We have a - right away, when we call this action, a dispatch that REQUEST_ROBOTS_PENDING and then we
fetch the users, we receive the users, and then we either have a success with the payload of the users
or an error with the payload of error.
Finally we go into the reducers and in the reducer we know that we want to import from the constants
the same actions that we had.
So let's just go to Constance or let's go to actions and copy exactly what we have here as well.
Because we're going to need all those constants.
And now down here we're going to create a new reducer because instead of combining all these reducers
we want to make them each specific to their use case.
This reducer worries about search robot, the new reducer is going to worry about the request.
Robots and once again with the same syntax we have state that's going to equal the initial state
and then the action that is going to have default empty object.
And within here an object or within here we'll have once again a switch statement that listens to action.type
and this action.type is going to have three possible states.
One is that case is REQUEST_ROBOTS_PENDING in which case we return object.assign
Again an empty object.
The state.
And then finally we're going to create a new state called isPending.
And this isPending is going to be set to true
because the request robots is pending currently.
All right.
The next thing we're going to do again we're going to have a case for this time.
The success and this is going to return object.assign an empty object a state.
And this new property robots that is action.payload and also we want to change the state isPending
to now be false
because we've gotten the response from the promise
and then the third case is when there's an error.
And in that case we're going to return object.assign
state and we'll just have an error state that we'll create that has the action.payload.
And once again we're going to want to change the isPending to be equal to false
and close the object. And remember always, with a reducer,
We want to have at the very end, always return the state if it doesn't match any of the criteria.
So again return state. Awesome.
Now you might be thinking to yourself initial state.
We have this but REQUEST_ROBOTS doesn't care about searchfield.
And in this case we want to create two different initial states: one for the search robots and one for
the request robots.
So let's rename this to initialStateSearch
and create a new state initialStateRobots that now has these three states that we mentioned here
that is isPending of false to start off with, a robot's array that is currently empty, and an error string
that is also empty.
So let's save that.
And let's see some errors that we get here and we just have a little bit of a syntax error.
Because we don't need this curly bracket.
There you go.
And again we want to change the initialStateRobots.
in the function to this one.
We save and everything looks good.
Let me make this a little bit smaller.
Now we have a problem.
We have to reducers now and if we go to index.js
we see that in the createStore function we're just using the searchRobots reducer.
So let's import now.
The other reducer that we have which is requestRobots and now add this into the store as well.
But how do we do that.
Well, we have a function that again comes with redux that's called combineReducers. And this combineReducers,
pretty self-explanatory, combines all the reducers into a root reducer.
So let's do that.
I'm going to say const rootReducer is going to equal the combinedReducers.
And this is going to and accept the, in an object form, all the reducers.
In my case it's the searchRobots and requestRobots.
And now this rootReducer can be used in the store to include both reducers
Let's save.
All right.
We're almost there.
Now because we just used combined reducers.
It's something that we've mentioned before, and that is if I go into app.js
and scroll all the way up to mapStateToProps, our state now no longer has searchField as part
of its property.
Instead it's going to have two properties searchRobots and requestRobots which each have their own
properties.
So now we have to update the searchRobots
like this which has the property of searchField.
So this is what you're most likely going to see when you're working in redux in mapStateToProps.
Now the final piece of the puzzle: redux-thunk. How does this work?
Well redux-thunk is a middleware that waits and sees sees if any actions return a function instead of
an object.
What does that mean?
If we go to actions we see that this is returning an object, but right here we're not returning an object
are we, as a matter of fact we're not really returning anything right now.
Thunk middleware is waiting for a function.
And if ever an action that goes through it -remember a middleware is just a tunnel that actions go through
and it notices a function it's going to act upon it.
So let's do that first and I'll explain in details how it works.
We first need to go to App.js.
And within here we want to update a few things.
First we want to update the state to now also include the robots to have state.requestRobots.robots
Again this is the state from the reducer.
There is also the isPending state again through their requestRobots.isPending.
And then finally the error state from once again state requestsRobots.error
The second part and the most important part is the request robot action.
Again something that replaces this componentDidMount request.
The way we can do that is within here add another property that we're going to pass on and we can name
it whatever we want we'll say onRequestRobots
and this is going to be a function, and this function.
Remember we don't want to necessarily just dispatch.
We want to - return a function from it.
In our case it's the requestRobots reducer and this requestRobots reducer or action has the
dispatch method.
All right.
I know that's a lot.
So let's just go through this one more time.
Right now we need the requestRobots
action and this requestRobots action needs a dispatch method to actually dispatch these actions.
So in App.js we first need to import our action which is requestRobots from the actions file and
this requestRobots needs to have the dispatch method.
This is the same as doing this.
And now this dispatch is going to work as long as we use redux-tThunk because it's going to catch the
fact that this is going to return a function.
So if we go back to our actions we can now do this.
All right, what just happened? Well, we've created a higher order function: a function that returns a function.
And I know this can get a little bit confusing but a thunk.
Because now requestRobots is going to return again because of error functions.
This is going to imply that it's returning this, it's going to provide the dispatch function to this
second layer function so that we can use it here.
Again this is something that you just have to get used to but in the end if we save this let's see if
it works.
Now I know it's the first time around so we've probably messed something up but let's check it out.
I refresh and everything works.
We're not getting any errors.
But remember, we actually need to connected to our app. If we go back we can now go to app.js and we
have all these props that we can use now.
So the componentDidMount no longer needs to have the fetch call. All he needs is this.props.
and the action that we have our requests robots onRequestRobots
perfect.
And we also don't need the constructor anymore do we.
Because there's no more states these robots are now going to be returned as part of the props from
onRequestRobots.
So again we're going to change this.state to
robots and we can also get isPending to check if isPending is true.
To render if this is true then we want to render loading.
If not we want to just render our robo friends.
So those are coming from our props
And now we only have componentDidMount in our app.
Let's save and go back to our app.
All right, look at that.
That doesn't happen very often but I somehow managed to code without any making - without making any errors.
Again that's a rarity.
Enjoy this moment.
I'm definitely enjoying it.
We see over here that we have "REQUEST_ROBOTS_PENDING" being fired and then SUCCESS being fired again.
Let's refresh quickly.
All right.
What happens if the request fails.
Let's go back here and go to our action.js
And let's just type something wrong here, we'll say like that.
Save it.
Go back and there you go.
We have "REQUEST_ROBOTS_FAILED".
Very cool.
Let's fix that.
And save.
All right.
We have our app that does the exact same thing as before.
Nothing too crazy just filtering our robots.
But the only difference is that we have this amazing logging ability and it's using redux.
Very cool.
I want to go over this one more time.
And this is because it is a bit of a sticking point for most people.
Remember this requestRobots, how it has this two functions.
The requestRobot, right now redux,
out of the box, wouldn't understand this, because we're not returning an object, as it expects for an action.
We're returning a function and this function, isn't going to mean anything to it. By adding redux-thunk middleware,
we're now listening to actions.
And any time the requestRobots action gets triggered it's going to return a function and trigger redux-thunk
and redux-thunk is going to say - oh this is a function I'm going to give you, here is the dispatch
so you can actually call some actions, and we can finally run our actions like this.
So if we go back to our diagram we've created a system where the actions are being triggered.
They go through any middleware.
If it's a search term change, it's just going to go straight to the reducer, run through a nice function
update the store and make changes to our view. If we request robots it's going to notice that it's a
function it's going to go into the middleware, and redux-thunk is gonna say.
All right first just dispatch pending to the reducer, and I'll let you know when I'm done with the promise
and I'll let you know if I got any robots.
And when it returns it's going to dispatch the success, go through the reducer, update the store and make
changes.
Phew! That was a lot.
But if you got this far and you understand it you've pretty much gotten redux.
That's it.
That's the whole redux library.
It's very very simple.
And once you start using it a couple times and you get comfortable with it it's really really beautiful
the way everything works.
But for now let's all take a break.
Thanks for watching and I'll see you in the next one.
Bye-bye