Build Your First API-Powered React App with Axios

We've seen the finished app, so now it's time to build it! This lesson will take you, step by step, through creating a brand new app, the Furry Friends Gallery Mark II.

Project Source Code

Get the project source code below, and follow along with the lesson material.

Download Project Source Code

To set up the project on your local machine, please follow the directions provided in the README.md file. If you run into any issues with running the project source code, then feel free to reach out to the author in the course's Discord channel.

This lesson preview is part of the Beginner's Guide to Real World React course and can be unlocked immediately with a \newline Pro subscription or a single-time purchase. Already have access to this course? Log in here.

This video is available to students only
Unlock This Course

Get unlimited access to Beginner's Guide to Real World React, plus 70+ \newline books, guides and courses with the \newline Pro subscription.

Thumbnail for the \newline course Beginner's Guide to Real World React
  • [00:00 - 00:11] Lesson 3, building the furry friends gallery, Mark 2. So here we are, ready to expand our API, fetching and handling knowledge using the Axios package and grabbing data from the dog API, dogs as a service.

    [00:12 - 00:18] We're going to move to this slightly different API provider for a few reasons. One, it handles pageing via the API.

    [00:19 - 00:25] Two, it deals with API keys and sending them via request headers. And three, it offers more flexibility in the data that we can receive.

    [00:26 - 00:35] Overall, however, the dog API will allow us to simulate a more realistic environment for requesting and receiving data in a realistic project. After all, this is the beginner's guide to real world React.

    [00:36 - 00:46] Our first move will be to set up a new project using the Create React app helper, so let's get started. So over in a terminal window, let's navigate to the folder that you want to create the project in.

    [00:47 - 00:58] Next, we'll type the Create React app command as follows. We'll do Yarn, Create, React app, not call mine furry, friends, too.

    [00:59 - 01:20] Let the command line finish installing the dependencies, waiting until you see the success message here and Yarn commands to start building the app. As we did in the last project, it's always best to spin up the default unchanged app we just made to make sure that we're starting from a solid, more importantly working best.

    [01:21 - 01:33] So I've cleared my terminal and then will follow the advice in your terminal output and enter the following commands of CD, furry, friends, too. And then we'll do Yarn, start.

    [01:34 - 01:45] Once the project's built and launched, you should be able to fire up HTTP, localhost, 3000, inner browser and see a dark background and a spinning React logo. You'll soon come to know and love.

    [01:46 - 01:53] Now we're going to do some cleaning up in the startup project. Create React app does load in a few bare bones files and styles to give you a jumping off point.

    [01:54 - 02:10] However, just like we did in the last module, we'll need to make a few changes to get everything cleaned up and ready for our new gallery app. So first, in your source directory, open index.js and remove the following line towards the top, which is the import index.css file.

    [02:11 - 02:20] After that, locate source index.css and just delete it. Next, find the app.css file.

    [02:21 - 02:30] Hire everything in there and delete that. Let's save the file. Finally, open the main app.js file located in source app.js.

    [02:31 - 02:41] This currently contains a lot of starter.js.js as we're going to replace as well as a link to the logo file that we want to remove. First, locate the following line towards the top with the logo in it.

    [02:42 - 02:45] Just remove that file. Just remove that line.

    [02:46 - 02:53] Now, select everything in the return statement. Everything between return, open parentheses, close parentheses and replace it with the following.

    [02:54 - 03:05] So that new return statement will look like this. We've got just a H1 and welcome to furry friends gallery.

    [03:06 - 03:18] Our project contains a few default files components as it's loaded by default, but we're not going to worry about those for now as they're not doing any harm just sitting there and they're not being loaded by anything anywhere. Now it's time to add some project dependencies.

    [03:19 - 03:29] So we'll create and edit the files we need to get our project running, but first we need to add a couple of dependencies to our project, one of which is bringing Axios on board. The first dependency to add is the Axios NPM package.

    [03:30 - 03:40] We've talked about the benefits of Axios already, but if we want to access those benefits and everything Axios offers, we need it to be a part of our app. Fortunately, it's very straightforward to do back in the terminal window.

    [03:41 - 03:52] Make sure you've got the root project location selected and enter the following command. So we're going to do Yarn, Add, Axios, and that's it.

    [03:53 - 03:54] Nice and quick. Now we've got to add Bulma.

    [03:55 - 04:11] We could add Bulma as a dependency just like Axios, but for familiarity sake, we'll add it in much the same way we did in the last project. So if we open up the file, public, and then index HTML, if you remember this is the template HTML file the project uses to render the initial output of the app .

    [04:12 - 04:19] Next we'll add the following line somewhere between the opening closing head tags. I'm going to add mine after the title.

    [04:20 - 04:35] And you can also edit the title of the page if you wish. Now that we're going to be implementing a data handler of sorts to act as a middleman between the API and our components, there are a couple more files that we're going to be using.

    [04:36 - 04:50] Number one, app.js, the familiar project starting point where all the magic happens. Number two, a dogcard info.jsx, a slightly modified component from the previous project that displays a dog picture and an ID value.

    [04:51 - 05:05] Breedlist.jsx, a self-contained data fetching component that'll handle its own data needs and display a list of breeds to filter our main picture list on. App.css will add a few additional styles in here to make the dogcard components look a little nicer.

    [05:06 - 05:15] A .env file.env, a new type of file that holds key variables that might change between environments. We'll start our API key and other data in here.

    [05:16 - 05:29] And finally, the api.js file. This will be our data handler like library that will be responsible for interacting with the API and returning it to the calling component. Before we do anything else, we'll need to obtain an API key.

    [05:30 - 05:41] Now there are a lot of free APIs out in the world, but most that you'll come across usually offer their wares from behind an authentication key. This helps limit abuse and helps the API provider to keep track of the volume of requests across a given range of accounts.

    [05:42 - 05:48] In that respect, the dog API, dogs at the service, is no different. Fortunately, it's really simple to request an API key from them.

    [05:49 - 06:02] First, head over to the dogapi.com, as you can see here, and click on the large blue button saying "Sign Up for Free". You'll be taken to the following page where you can enter your email address and brief description about what you'll be doing with your app.

    [06:03 - 06:12] After hitting "Sign Up", you'll see a thank you screen and a message to check your inbox for the key. If you head over to your email, you should receive an email containing your shiny new API key like this.

    [06:13 - 06:33] I've blurred mine out for privacy reasons, but with your API in hand, it's time to take a quick look at the documentation, which is another important aspect of your role as a front-end developer, especially when working with APIs. How in a real-life front-end role you'll very much likely come across a situation where you'll have to connect to APIs somewhere out in the wild to fetch important data for your UI.

    [06:34 - 06:47] When facing this, you'll hopefully have access to a quality API documentation that outlines what endpoints are available, as well as has information that explains how to call the endpoint. What parameters to supply, what the format of the data returned is, and so on.

    [06:48 - 06:59] With that in mind, let's take a quick look at the dogapi documentation. So back over to the dogapi.com, be head over to the docs, and you'll see a screen similar to this.

    [07:00 - 07:15] The top links in the left-hand sidebar all refer to features and functionality of the API, along with details of how to authenticate to the API in order to use it. Under the API reference heading, however, is where we'll find all the information about the specific endpoints that the API exposes.

    [07:16 - 07:32] So let's click on "breeds", and then click on "list the breeds" to view that page. You can see the page contains information about authorization, which parameters you can supply to the /breeds endpoint, in this case there are three, as well as the response data that you receive following a successful call.

    [07:33 - 07:44] The most useful part of this page for me is to send a test request section down the bottom. If you hit the orange send button, the API will generate a sample call with real data.

    [07:45 - 07:58] Note the API URL here, api.the dogapi.com, version 1, breeds, will be making use of this in a moment. And you see the response from the API call looks a little bit like this.

    [07:59 - 08:10] This is key as it helps us shape the JSX in our components, now that we know what the data is available and what the shape of that data is. Now we've got to create our .env file.

    [08:11 - 08:21] So a file without a name, but with some sort of variation of .env as the extension, is an environment file. It contains environment variables, which are pieces of information specific to a particular development environment.

    [08:22 - 08:40] For example, you might have a staging file and a production one, each containing the same variable names, but with different values, each specific to their respective environments. There's no hard and fast rules to what you can keep in such a file, but generally you'll start relatively insensitive information that changes between different environments, such as URLs, names of things, or version numbers.

    [08:41 - 08:55] For us in our project, however, we'll start the API URL and our API key in the .env file of our very own. In the root of the project, let's create a new file, but don't give it a name, instead just give the file extension directly, so it should read .env.

    [08:56 - 09:09] With that done, open the file and we'll add the following information. Notice the API URL that we noted before, this API.the.opi.com.

    [09:10 - 09:15] This is the base URL to call the dog API. We'll be appending some specific routes like breeds later on.

    [09:16 - 09:30] The important point to remember here is to replace your API key in square brackets from the documentation with the API key the dog API sent to via email. Now generally, with .env files, you can put whatever variable and value combination you like in there.

    [09:31 - 09:40] However, when it comes to the Create React app, if you want to use any of the variables in here, you'll need to prefix them with React_app. Like we've done that.

    [09:41 - 09:53] Or they won't be read. For example, if you want to use a variable called dinosaur, you must name it, React_app_dinosaur in here. Create React app does this to avoid exposing anything that it shouldn't that shares the same name.

    [09:54 - 10:03] You can read more about environment variables and the Create React app in their official documentation. Now it's time to create the dogcardinfo.jsx component.

    [10:04 - 10:14] The dogcardinfo.jsx component is very similar to the last one we created as part of the previous project. We do have a couple of small tweaks because of what data is available to us from the dog API though, so let's get going.

    [10:15 - 10:28] If you don't have a components folder under your source folder, then let's create that now. Next, create a new component file within this new folder and call it dogcard info.jsx.

    [10:29 - 10:41] Now let's copy in the component body. There's nothing particularly special or fancy about this component.

    [10:42 - 10:54] It's merely a repeatable presentational component that will be used to show a nicely styled picture of each dog picture that returned from the API. The main difference here is that we've brought in a picture ID value here that 's unique to each picture.

    [10:55 - 11:14] We're going to use it to display it under the picture of each dog purely for presentational reasons. Now if we open up our app.css file with the existing app.css file that comes with the Create React App Project starter, when you took date or styles with our dog pictures and radio buttons for breeds , look much nicer.

    [11:15 - 11:26] Open up the app.css file and we've already replaced the default starter contents, but we've got a copy in the following. As well as making each dog picture a card component have a sensible height.

    [11:27 - 11:35] We're making sure each breed radio button is displayed on its own line and playing with the space in a little bit. Now for the really interesting part, fetching our data.

    [11:36 - 11:47] So we'll start by creating a new folder called lib under the source folder. Next we'll create a new file in here called api.js.

    [11:48 - 11:58] So make sure you've got the api.js file open and let's start filling out the API methods. With the api.js file we'll be introducing the concept of a data handler.

    [11:59 - 12:14] Put simply a data handler is a middleman of sorts that handles the communication between a data source and a data consumer. In our case the data source is the dog API and our data consumer is any component that wants information from the API, such as the breed list component .

    [12:15 - 12:29] You could of course just put the code directly in each component which is fine for smaller projects, but as soon as you start to repeat this API handling code things start to get messy. If you need to change something in the api fetching code you need to change it in multiple places.

    [12:30 - 12:38] Your components grow larger with additional api handling methods. Your components start to widen their responsibilities and handling errors becomes more complex and distributed.

    [12:39 - 12:54] However by abstracting this data fetching a handling facility into a single place, the data handler, we centralise all of this logic and responsibility into one place. In reality you might have a few data handlers that deal with different areas of the api, but the point of the data handler remains the same.

    [12:55 - 12:59] We gain an awful lot of benefits. Only one place to edit, maintain data handling code.

    [13:00 - 13:17] Our component becomes more dry, that don't repeat yourself, and adheres to the single responsibility principle, or SRP, more closely as it should now be dealing with just handling incoming data and presenting it. Errors are now localised to one place and we only need to define one interface with the api.

    [13:18 - 13:35] We'll be diving into data handlers in more detail in an upcoming module, but for now hopefully you can get a glimpse of how useful they can be to any project that deals with fetching and handling data. So back in the api.js file, the very first thing to do is to bring in Axios and our environment variables, which we defined earlier.

    [13:36 - 13:49] So we're going to import Axios from Axios. Notice the process.env there, which is how we reference any environment variables.

    [13:50 - 14:02] Next we need to define the call api function, which will actually go and talk to the api. We'll accept a URL, some params, which will leave us null by default.

    [14:03 - 14:13] That would be arrow function, and then we'll fill in the body. So this call api function accepts a URL and a params parameter.

    [14:14 - 14:23] The URL will be the endpoint of the api we want to call, whilst the params will be an object of any key value pairs that need to be passed to the api. But let's break this down a little bit.

    [14:24 - 14:32] First up is an object called request config. Axios accepts a configuration object with a list of settings and options that you can pass in to tweak your calls.

    [14:33 - 14:51] For us we need to pass in the api URL here, from our environment variables, to the base URL property, from Axios. We also need to supply an x-api-key value to the headers object with the api key value, so that any api requests are authenticated and validated by the dog api.

    [14:52 - 15:04] Finally we supply the URL parameter as passed into the call api function. Axios will combine the base URL here, and the URL value here into a final api endpoint.

    [15:05 - 15:14] Next we do a quick check to make sure that params isn't null. If it has a value, we add it to the request config, and pass it to Axios.

    [15:15 - 15:26] After that it's a simple case of calling Axios, passing in the request config object. We're wrapping the call in a try catch block, just in case and handling the error by logging it out to the console.

    [15:27 - 15:40] Note in a production app you'd want to handle the error in more detail, more gracefully than we have here. For example you'd typically see some kind of log entry made, and a message sent to the user to explain what happened in any next steps.

    [15:41 - 15:52] So without a generic call api function in place, we can move on to separate functions that request specific api endpoints and handle their return data. The first of those rows will be the fetch breeds command.

    [15:53 - 16:10] So we're going to define this one as an export, a const, fetch breeds, it's another asynchronous function, and it will accept a page and a count which will default to 10. I'm going to just copy in the body.

    [16:11 - 16:27] So we're employing the async await combination again in our method here because we're dealing with asynchronous behaviors. So first we call the call api function that we pass in the breeds URL along with a params object that populates the limit and page properties.

    [16:28 - 16:41] These are used by the dog api to determine how many results to return per page as well as which slice of results, i.e. which page, from the entire results available. The dog api documentation explains which parameters are available and what they do.

    [16:42 - 17:00] Once the data returns we simply supply it back to the caller of fetch breeds as an object with the breeds value, being all the breeds data that the api returned to us as well as a total breeds value, which will be a numerical value of the total number of breeds that the api has to offer. We'll be using this later to page the results.

    [17:01 - 17:11] The last function to define in the api.js file is the fetch pictures function. Here we'll be expecting a list of pictures of dogs that belong to a specific breed.

    [17:12 - 17:29] So let's define that now. It's going to be another export, const, fetch, pictures, it's asynchronous again. This will accept a breed, which by default will be nothing, and a count which will default to 20.

    [17:30 - 17:39] Looking at our body in here, we'll just copy this in. We really need to know the breed in order to return a specific picture for that breed.

    [17:40 - 17:54] So if the breed parameter is empty or not populated, like here, then we'll return an empty array. This saves us an unnecessary api call, but it also prevents the calling method from receiving a null value, an empty array is much nicer to deal with.

    [17:55 - 18:08] All being well, we'll call the call api function once more, supplying an api URL of images/search and another params object specifying the breed and the number of pictures to return. We've defaulted this to 20 in the arguments.

    [18:09 - 18:17] Finally, we just return the pictures data that the api returns to us. Oh man.

    [18:18 - 18:29] Creating the breeds list.jsx. The breed list component is going to ask our data handler for a list of dog breeds, display them as radio buttons in pages of a certain size and handle page in between them.

    [18:30 - 18:44] So we'll start by creating a new file in the components folder, which we'll call breed list.jsx. Make sure you've got the breed list.jsx file open, and as always, we'll start with our import section.

    [18:45 - 18:57] So React has to be in scope, but we'll also be using use effect and use state hooks in a moment, so bring them along for the ride too. After that, we need fetch breeds function from our api.jss file to fetch the list of dog breeds for us.

    [18:58 - 19:20] With the import done, the best next step is to fill out the empty or bare bones component and default export for the file. So we'll just do an inline export, bring in a proper dispatch breed change, define our arrow function, and we'll simply return for now a fragment with a little to do message.

    [19:21 - 19:30] We'll do our semicolons, and there we go. We're destructuring the incoming props object to just a single argument dispatch breed change.

    [19:31 - 19:41] This will be a function passed down from the parent component that will be called when a user selects an individual breed. Remember we're keeping things within compartmentalized areas of responsibility.

    [19:42 - 19:56] In practice, this means our breed list component will list out some breeds and handle paging through them, but it doesn't know or care about what happens when someone selects a breed, other than keeping track of which selection they're made. That job will fall to the parent component, which will be the app.js file.

    [19:57 - 20:05] But we'll cover that when we're finished here. For now, our breed list cares about is that once a user selects a breed radio button, it can call the dispatch breed change function and carry on its way.

    [20:06 - 20:17] With our skeleton component in place, the first thing to do is to define some variables. So right at the top of the component, we'll create our variables.

    [20:18 - 20:26] In order, here are the uses for the variables. With value, this will keep track of a currently selected breed value from the list of radio buttons.

    [20:27 - 20:39] With breeds, we're going to keep the list of selectable dog breeds in here and update it each time the API returns. It's loading, a simple boolean flag that sets it true when we're doing any API fetching and false the rest of the time.

    [20:40 - 20:50] We can use this to show or hide parts of the UI depending on if we're loading or not. With current page, we'll need some players to keep track of the current page of dog breeds that the user is on, this is that place.

    [20:51 - 21:02] And finally, with total pages, we need to know the total number of pages in order to disable paging at certain limits. We'll track that value instead here and update it each time the API updates the breeds list.

    [21:03 - 21:10] Now, each time the user selects a radio button, the onChange event is fired. We'll capture it using the following function, handleChange.

    [21:11 - 21:20] So right under our variables, we'll define const, handleChange. Now, there we'll paste in our body.

    [21:21 - 21:26] This function should look quite familiar by now. Handling input changes via event handlers is often done in this fashion.

    [21:27 - 21:46] You can see that we update the value instead using the hookset value with the e .target.value provided to us via the synthetic event. The extra bit we do here is to inform the parent component that something has changed by calling the dispatch_breedChange function from props after checking that it's not a null or empty value.

    [21:47 - 21:56] Notice how we're passing the e.target.value to dispatch_breedChange, not the value from state. You might be tempted to do this, but you'll encounter some weird and unexpected results.

    [21:57 - 22:08] Each time a user changes a page of dog breeds using a next breed or previous breed, buttons, we'll be calling this next function, handlePageClick, which accepts a new page number argument. Let's quickly outline that.

    [22:09 - 22:24] So we'll have const, handle, pageClick, an argument of new page number. The purpose of this function is to update the newly chosen page number instead using setCurrentPage and passing in the new page number argument.

    [22:25 - 22:36] However, we need to do a quick check beforehand just in case the user somehow managed to make a potentially faulty choice. We check the new page number and if it's less than zero, page one of the results is actually page zero in the array because the array is a zero index.

    [22:37 - 22:53] Or if it's greater than or equal to the total number of pages, we return from the method without doing anything to prevent errors in the UI from accessing pages that don't exist. The last piece of the puzzle with our functions before we move on to the JSX is to handle the fetching and processing of our dog breeds data.

    [22:54 - 23:05] This is the perfect job for the user effect hook. Let's implement that now. So under handlePageClick, we'll define user effect, passing in an anonymous function.

    [23:06 - 23:13] And using current page as in the dependency array. We'll copy in some contents.

    [23:14 - 23:20] There we go. So notice how we're passing in the current page variable instead to the dependency array of the user effect hook.

    [23:21 - 23:31] Every time current page changes, this user effect hook will fire. We're defining an asynchronous function loadBreaths, which in turn calls the fetchBreaths function from our API data handler.

    [23:32 - 23:40] We pass it the current page in an arbitrary page size value of 15. You can make this whatever value you wish and it will increase or decrease the number of breeds returned per page.

    [23:41 - 23:48] The function follows a pretty simple flaw. Number one, we set the is_lord_in_value to true so that you are an update.

    [23:49 - 23:57] Two, we call out to the API to get a list of breeds. Three, we update a state with the breeds once the API returns.

    [23:58 - 24:09] Four, we update state with the total number of pages. This calculation divides the total number of breeds returned from the API by the pages we want, i.e. here we're using 15.

    [24:10 - 24:22] We use the math.seal function to round this figure up because if we get something like 14.4 pages, we can't 0.4 of a page, but we can't have a 15th page. And parse it as an integer with the built-in parse int function.

    [24:23 - 24:33] Finally, we update the loading value instead back to false to update the UI again. And of course, the last move to make is to generate some JSX to return to the user.

    [24:34 - 24:45] We'll start by adding a conditional block of JSX that we'll show when is_lord ing is set to true. So we're going to replace this to do statement with the following.

    [24:46 - 24:58] The progress element is a native HTML element, but we can style it up with Bul ma to give it a little more pizzazz. Next, we need to add an opposite block of JSX for when is_loading is false, and this will be what will be displayed by default.

    [24:59 - 25:08] So add that straight afterwards. Again, we're leaning on the Bulma framework specific way of defining radio buttons here, making sure to assign handle change.

    [25:09 - 25:18] Here to the radio button specific on change event. For the previous and next Paging buttons, we assign handle page click to their on click events.

    [25:19 - 25:32] Here, the only difference is that we pass in a value of the current page one lower or higher depending on if the user clicks previous or next respectively. So that's everything added to the Breedless component.

    [25:33 - 25:35] So we'll give it a save. And we're done.

    [25:36 - 25:47] Now it's time to edit the app.js file. So whilst.card info and Breedless take care of their respective areas, the app component really orchestrates everything and wires up the two.

    [25:48 - 25:58] We'll start by mapping out the app component with the import we'll need. So we need to import, react, use state and use effect.

    [25:59 - 26:10] Now we need to import fetch pictures, our API, add some components and the styles we're already imported. So there shouldn't be anything too unfamiliar here.

    [26:11 - 26:23] We're pulling React into scope as well as use state and use effect hooks we'll be using later on. We need to use the fetch pictures function from our API data handler as well as the two components that display the breeds and the dog pictures.

    [26:24 - 26:33] Lastly, we need to make sure that the app.css file is pulled in otherwise our styles will be applied. The next thing to do is to define some variables.

    [26:34 - 26:45] So going through them, pictures will be used to keep track of the returned picture array from the API. Selected Breed ID will hold its name sec which will be updated from the Breed less component when the user selects the breed.

    [26:46 - 26:57] Is loading will act the same way as it does in the Breedless component to update UI changes based on the loading state of the API. Now we need to fetch some pictures using the user effect hook.

    [26:58 - 27:14] So just like the Breedless component, the user effect hook is a great place to take care of fetching data from the dog API when one or more conditions change. In our case, we want to fetch an updated list of dog pictures from the API when the user selects a new breed i.e. when the selected Breed ID value instead changes.

    [27:15 - 27:25] So we'll define our user effect hook here. The user effect, pass it an anonymous function and then make sure in the dependency array we have selected Breed ID.

    [27:26 - 27:37] Then in the contents, I'm just going to copy in the contents here. So it has a very familiar look and operation to the user effect hook we just find in the Breedless component.

    [27:38 - 27:43] We update the UR loading state here. We fetch the pictures from the API.

    [27:44 - 27:50] Update state with the picture data once that returns. And reset the load instead to false.

    [27:51 - 28:03] Notice that we're passing in selected Breed ID to the dependencies array so the user effect hook fires whenever this value instead changes. Now we need to complete our app component by building out the return JSX.

    [28:04 - 28:11] So with all that logic in place, the final thing to do is to outline the UI in JSX for the user. Our return statement will look something like this.

    [28:12 - 28:28] So we're going to replace our placeholder title and copy in the final JSX. So the upper half of the JSX is a simple header tag here with a title and a strap line underneath.

    [28:29 - 28:45] Following that we're using Bulma's columns class which allows us to define two columns, one narrow and left hand column for the sidebar with a larger right hand panel for the picture gallery. In the left hand column we'll add a title, search by Breed, over here and then our Breedless component.

    [28:46 - 28:56] Now remember that dispatch Breed change function that we called in the Breed less component when a new Breed selection was made? Well this is where we're going to pass a function down into the Breedless component to handle that.

    [28:57 - 29:11] The proper dispatch Breed change here passes in an inline arrow function which receives a Breed ID, an update state using the set selected Breed ID function. You could absolutely use a separate function here and that would work just fine .

    [29:12 - 29:25] However it seems a little over the top to do that when all we need to do is update a single value instead. In the right hand column we use the same two is loading logic blocks from the Breedless component to display an alternate UI depending on if we're currently loading data from the API.

    [29:26 - 29:43] If we're not loading and we have a list of pictures we'll use the array map function here to return a new array of JSX elements, each of which includes a Bulma column that wraps a dog card info component. Here passing in an image URL and picture ID values.

    [29:44 - 29:57] All that's left is to make sure all of the files are saved and to run the project. So back in our terminal we will type yarn start.

    [29:58 - 30:11] After a moment or two the project will finish building and depending on your setup a browser window should open and display the development URL. Take a look at the running app and have a play about with it. You'll see a list of available breeds in the left hand sidebar complete with previous and next buttons.

    [30:12 - 30:20] Have a browse through the pages of dog breeds until you find one you like and then make a selection. I'm going to go with the Australian cattle dog.

    [30:21 - 30:29] So once you've selected a Breed radio button you'll see the right hand panel updating with a list of filtered dog pictures that match your chosen breed. Exciting stuff.

    [30:30 - 30:39] Congratulations you've just built a fully working browseable gallery app that pages a list of breeds for an API. Then it responds to user input retrieving a filtered list of images from the same API.

    [30:40 - 30:45] All the while we've been using Axios under the hood to provide a great cross browser data fetching experience.