This video is available to students only

Building the services

This lesson focusses on building the data-handling services and core Redux management system for the Dinosaur Search App

Building the services and core structure#

We’re going to introduce something a little different in this final project, the concept of services. If you’ve been developing for a little while, the idea of a service layer won’t be anything surprising or new to you, but for the rest of us introducing some data-handling services will give us some separation between different layers of our app.

Ideally, the frontend UI should just concern itself with asking for data, receiving it and then displaying it to the user, allowing them to interact with it. It shouldn’t know (or care) where this data comes from, or how.

That’s the idea behind creating our various [name].service.js files that we’re going to build in this lesson.

However, for now let’s start with implementing a Redux system using the useReducer Hook and the Context mechanism, as this whole process should be fresh in your mind from the previous module.

initialState.js#

We’ll begin by opening the initialState.js file in the /redux folder. Add the following initialState object in its entirety:

You might remember that this offers us a good at-a-glance starting point for the sort of structure we want our app’s state to take. We’ll plug this file into our reducers to effect change upon it as each reducer function is called.

At the moment, however, you can see that we have two slices of state: auth and dinos. Each has a loading flag set on it (which we can use to toggle some sort of loading UI in the components) and you can see that the dinos slice has a favourites array where we’ll keep track of our favorite dinosaur id values.

At this point, you may be starting to suspect some British involvement in the development of this course! Of course, you would be right (I am a UK developer after all). That's why the Favourites component and functions dealing with the favoriting and unfavoriting of dinosaurs contain the British spelling of the word "favourite". Feel free to change these however you wish, just make sure to update all the file and function references from to share the same names or you'll run into errors when you run the project.

Save the file and let’s move on.

authReducer.js#

As you may expect, the authReducer.js file will handle state updates that relate to the auth slice that we’ve just seen. Specifically we’re interested in a few state changes:

  • Trigging a loading status change upon signing a user in.

  • Updating the user object when we’ve successfully finished signing in.

  • Performing a state reset when the user signs out.

Start by creating a set of actions:

The actions variable is just a plain JavaScript object that houses some hard-coded action strings. Next, it’s time for the physical reducer code:

You can see we have three different switch cases to handle the three scenarios we outlined earlier. Each one returns a new copy of the state object, only changing those parts that it needs to.

The only time we’re concerned with using the action argument passed to the reducer is when the user has successfully signed in and we get a user object back — you might remember this from the previous lesson where we explored the API.

The complete file#

The file in its entirety now looks like this:

dinoReducer.js#

The dinoReducer.js file is going to look very familiar to the authReducer file in its approach. This is something I highlighted in the previous module on Redux, where things might look a little alien and complex to begin with, but once you’ve built a Redux system, extra additions to it start to look familiar.

Let’s define this reducer’s actions:

This time we have four actions, two for fetching dinosaurs and two to handle the favoriting and unfavoriting of a particular dinosaur.

Let’s add in the reducer body:

The first two switch cases essentially just alternate a loading property from true to false and vice versa. I don’t think it hurts to have this loading state change happen in two separate reducer cases for our learning purposes, but you absolutely could create a TOGGLE_LOADING_STATUS action and just flip the loading boolean to its opposite state in one shot.

Further down where we have the favorite-handling parts there is a little more logic, but nothing too complicated. In the first, when the user favorites a dinosaur, we return a copy of state with the action.payload value (which will be an id string) tacked onto the end of the favourites array.

Conversely, when a user unfavorites a dinosaur we need to perform a slightly bigger code dance to filter the current favorites in state , removing the id value that matches the action.payload value, and then set the favourites property in state to this new array.

The complete file#

The completed reducer file should look like this:

reducers.js#

Open up the reducers.js file and let’s pull everything together to wire up the various parts of our Redux system.

Here’s the code that’s going to power things:

This might look like a lot to drop in all in one go unexplained, but we’re not going to dwell on the details here. The keen-eyed among you will notice that this is almost identical to the reducers.js file from the last lesson in the previous module on Redux. The only difference is that this time we have two reducers to import, namely auth and dinos.

We import those reducers and pass them to the combineReducers function which will smush them together and handle different updates to different slices of state for us, whilst we just worry about calling a single dispatch function to do the job.

If you would like to revisit this file and how it works, please head over to the previous module on Redux for a full breakdown and step-by-step walkthrough of what each part does.

Save this file. Now it’s time for some services.

Services#

As we explained at the beginning of the lesson, building a service layer gives us a greater degree of separation between the different parts of the app. By building out some service handlers we can remove the responsibility of talking to the API from the UI components. They don’t need to concern themselves with talking to the Redux store either.

With service handlers in place they just need to ask a particular service for data, receive it and then process it accordingly. What’s also nice about this approach is that later down the line we could change the service handler to use a JSON file instead of an API, or talk to a database directly, and the components calling this service would never need to know about it.

Each service will manage a particular aspect of data interaction, such as authentication and dealing with the API. Each one will talk to the API, supply and request information as appropriate and call out to the Redux store to dispatch any updates to our app’s global state system.

api.service.js#

Let’s start with the api.service.js file. This will be a service to service other services (try saying that three times fast!). Essentially the API service will be the direct link to our API. It’ll handle any and all API calls, formatting incoming data and returning any response from the API to the caller.

Open it up and let’s fill it out, starting with a bare scaffold:

We’re pulling in axios to help with our physical API calls. Next we have a baseUrl variable which is a simple string, /api. At the moment all of our API calls start with this string path, but we don’t want to have to litter each function we create with it, and if it changes that means more work. We can stash it in a variable here for reference later.

Next, the getUrl function is a simple, one-line arrow function that uses JavaScript's string templating syntax to return us a resulting API endpoint that begins with /api and appends whatever URL was passed to it as a parameter.

Finally we have a barebones JavaScript class ApiService that contains two methods, get() and post() which will handle GET and POST calls to the API respectively.

Adding the axios calls#

Let’s flesh out the two class methods:

In each method we call an axios function, get() for get() and post() for post(). We pass some additional data to the post() function, but each case returns the asynchronous promise to the caller.

The complete file#

The completed API service file should now look like this:

With this first service complete, I must admit that it doesn’t look very exciting at the moment, nor does it do anything particularly exciting yet. However it gives us a great starting point to extend the interaction with the API. Even if it’s doing little more than calling the API and returning the response to the caller, we could add logging in here, intercept the request or response, inject authentication bearer tokens, or a myriad of other things if we so choose.

 

This page is a preview of Beginner's Guide to Real World React

No discussions yet