Instructor: We can use this API to get some data from a real service. In particular, we're going to look at this endpoint. If we paste that into the terminal, pipe it through JQ to make it look a bit prettier, you can see that we get an array of objects.
We'll make an epic to face this data immediately. We'll create a new file, epics, and then we'll call it fetchBeers. This is that API endpoint we just looked at. We just export a single function from this file. This is going to be the epic.
Now, the only requirement of an epic is that you return a stream of actions. If we want to face this data immediately on page load, we can just return Ajax.getJSON. This comes from the rxjs/ajax package, which we need to import.
We'll say ajax from rxjs/ajax. Then we can pass along the API URL. Now, before we use this in our interface, we can just do a debugging trick, and we can pipe this into the top operator, which we can import.
This will allow us to observe elements that are coming through the stream. Then we can ignore elements, because right now, we don't want this epic to actually produce any actions back into the Redux store just yet.
We just want to observe whether or not we're fetching this data correctly. We have this epic. We need to register it with our middleware. If we go back to the configure store, where previously we had this sample epic, we can remove that now, and we can import our fetchBeers epic.
We import it like that. Now, if we go back to the browser, we should see that that network request happens on page load. If I refresh, you can see it happens again. If we just check the network panel, filtered to XHR, we can see that that beers endpoint is being called.
To use this data within our application, we need to save it to the store. We'll create a reducer that's sole purpose is to handle this data from this beers API. We'll call it beersReducer. We'll have some initial state, which in our case can just be a property data, to hold the array of beers, and a flag to say whether or not we're loading.
Then we can export the function that will be the actual reducer. This will take state, and its initial value will be that initial state, and the action. Then we can just switch on the action type, and in the case that it is fetchFulfilled.
Whilst we're here, we'll actually create these in a separate file, so we can have beers, actions, and we'll export a constant. Then we can use that in our reducer. In the case that it is a fetchFulfilled action, we're going to return spreading the state in, setting the loading flag to false, and setting the data to the payload.
As we saw earlier, the API returns an array. We start with an empty array here, so this is OK. The default is to return the state unmodified. Now, we need to go and register this reducer back in the configure store file.
Where we have this appReducer already, we can create another namespace, so to speak, pass in our beers reducer, and import it. Now that we have a place to store the data, we need to go back to the epic, and ensure that we dispatch the action, fetchFulfilled, when we have the data.
If we go back here, we can simply map the response. We'll import that from rxjs/operators, map the response, which in our case will just be an array, into the action that has a type of fetchFulfilled. We'll create a function to help us do this. These are known as action creators.
In our case, we accept some beers, and we return the type, fetchFulfilled, and a payload of beers. Now, we can use this function from within our epic, call it here, and we pass along the response. Import it, and now, if we go to the browser, we should be able to see the full life cycle.
If you go to the Redux dev tools, you can see that we got a fetchFulfilled event. As the payload, it was an array of beers. The difference it made to the state was that the data property now contains those beers, and loading was set to false.
We fetch the data from the network. This produces a stream that has a single element in it being the response. We take that response, and we map it into an action, which just has this shape. This then ends up inside our reducer, and we save the data to the store.
To view that data in a component, let's create a beers list. This component is going to get these two properties from the store via this connect method that we've imported here from react-redux, and we use it to wrap our components.
This selector function here gets access to the entire state, and we just pull off the beers property. This part is coming from where we configured the store, we said that beers here is responsible for the state under the beers reducer.
Now, we can use this component from within our application component. Get rid of all of this, just put the beer list in there. Actually, that's going to import the named export, which is not the Redux-connected version of the component.
If we go here, you can see we have two exports, the component itself, and then the connected version as the default. We just need to make sure that we use the Redux-connected version like that, and if we view it in the browser, you can see that we got 25 beers, the fetchFulfilled happened. We can see that the network request gave us the response that we expected.