In this lesson we’ll learn how to perform an Ajax request to an external API in response to an action in the redux store. We’ll see how, within the epic, we get access to actions after any reducers have been executed and how we can convert that incoming action into a resulting one that will save the data received into the redux store.
Instructor: [00:00] This epic will cause this API to be called immediately on page load. If instead we want to fetch the data on demand, we'll need a slightly different approach.
[00:10] If we look at the reducer where the data is going to be stored, let's change loading here from instead being just a simple flag to being a status instead, a status that can have multiple different values. We want to enable the epic to be able to set this status based on what it's doing.
[00:31] If we add another case in here, and we'll have this set status constant, which we can add here, if we see an action of that type, then we're just going to set the status to whatever came through. We'll add an action creator for that. We'll just copy and paste this. We'll say this one is called set status. It receives the status and sends it through.
[01:02] That will allow the epic to set the status of that data. We also need something to actually trigger it. Let's add one more of these action creators that we'll call fetch data. It won't take any parameters, so there will be no payload. We can just add it here. This is something that one of our components will call that produces an action of type fetched data.
[01:35] Now in the epic, instead of making this AJAX request immediately on page load, we can do it in response to that action. Every epic that you register with Redux Observable gets access to a stream of actions. This is every action that happens in the Redux store. We can return actions.pipe.
[01:56] Then we want to filter this stream so that we're only responding to actions of that particular type. We could actually use the regular filter from RxJS, but Redux Observable actually has a helper to do this for us. It's just the function of type that we can import. Here we can give any number of string arguments.
[02:18] In our case though we have this fetch data constant that we want to use, so we can just say of type fetch data. When that action occurs, we want to do a switch map. Again, import that from RxJS operators. Looking back at our reducer, we want to set our status to be pending before the AJAX request happens. We can concat and we'll import this from RxJS, not from operators.
[02:48] This is a higher order observable that takes other observables. Because we want to first produce an action that sets the status. We'll import off from RxJS as well. Then if we go to our action creators, we have this set status. We'll call that and we'll say that we're currently pending. Now we can just grab the previous thing that we had and give that as a second parameter to concat.
[03:16] For every action that has the type fetch data, this function will be executed. We're returning an observable that is first going to produce an action of set status pending and is then going to follow up with an AJAX request that if successful will map its response into another action called fetch fulfilled.
[03:36] We can check the browser now to verify that we are no longer fetching the beers automatically. To trigger the AJAX request, let's add a new component. We'll just call this beers. This is going to be responsible for dispatching actions back into the Redux store as well as retrieving data from it.
[03:55] We export a component called beers and we access its props. We get data and status from here because we wrapped the component in a call to connect and we selected the beers namespace from the Redux store. Inside the component we have the inputs, which is going to be a button that will be disabled when the status is pending.
[04:26] In the epic, the first thing we do is dispatch this action of pending. That will go into the reducer here, the status will become pending. In this component it means this button will then be disabled. We need to add an on click handler. What should we do on click? In the beers actions we have this fetch data action creator.
[04:54] If we go down to the connect method, the second argument here can be an object literal that contains functions. If you give it like that, React Redux will add fetch data as a prop here. When we call it, it will dispatch that action into the store. We can just give fetch data here.
[05:17] When we go into a pending state, we're also going to show this spinner which is just using a file in this directory here. Then if our status ends up being success, then at the moment we are just outputting the data onto the screen. What we can do is repurpose this beer list to be a completely stateless component and just output the list.
[05:40] If we go to this beer list, it no longer needs to be connected to the Redux store. We can remove this line all together. We'll just replace the body with a very simple list that maps over the beers and outputs them with a thumbnail. Now I have this component that's in charge of dispatching actions and deciding on what status the data is in. If we get to success, we render the list.
[06:08] If we go and see how this looks in the browser, we can see we get this error. That's because back in the app we are no longer using this beer list as we see it here, rather, we want this beer's component. We want the default export for it.
[06:25] We'll just say beers from beers. Change that here, refresh and now you can see we are presented with this button. We click the button, we get a loading spinner and we can see that we went from fetch data to set status to fetch fulfilled. We're not seeing our elements here.
[06:48] This is where the dev tools really help us because I can see that here on a set status action we correctly set that to pending while the AJAX request was occurring. When it was successful, we're actually setting loading false. This isn't correct. We should have set status success here. Now we can go back into the reducer and you can see that here we're setting loading false, but instead, we should put status success.
[07:22] Now if we reload and hit fetch beers again, now we see another error, cannot read property map of undefined. This is the type of error that you can prevent through things like React PropTypes or using a static type system like Flow or Typescript, but those are outside of the scope of this course.
[07:37] For now, let's just check in here we check props.beers. That means wherever we use this component we need to send through property called beers. In our case that's going to be the data that we get from here. Try again and this time it works as expected.