How to Combine React Components Into an App

In the final coding lesson of this module we'll flesh out the UI components that will power the Dinosaur Search App

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:09] Lesson 4, Flushing out the components. So here we are, the final coding lesson in the course and the one that will make all the magic happen in our dinosaur search app, the components.

    [00:10 - 00:21] In this lesson we're going to work through the components we defined right at the start of the module and craft out a UI that will be powered by the hard work we completed in the last lesson. Let's start with the layout.jsx component.

    [00:22 - 00:32] The layout component is a simple and presentational based in nature. We used it in the app component to wrap a set of child components in some common styles and structure, including things like a header and navigation.

    [00:33 - 00:45] Now it's time to put those common elements in place and we're going to define the entire file in one go since it's quite small. We pull in the navigation component which we'll build out next and then we define the next part the layout component.

    [00:46 - 00:56] We're destructuring the incoming props argument past to the component, pulling out the children value. The children properties one of a few default properties on each props object that's passed to a given component.

    [00:57 - 01:17] A lot of components have children but you don't always make use of this children property unless you want to simply pass them along and render them as intended but unaffected, like the layout component is doing here. We wrap the navigation component in a header element and then add a semantics section element and a child container with some scant Bulma classes on them to give them some padding and other spacing.

    [01:18 - 01:41] Because this component will be applied to any route that's rendered by the app we can ensure that each separate page in our app will share the same foundational structure, header and spacing base styles. The navigation component is almost entirely presentational in nature in that it has minimal logic in place and even then what it does have is a series of toggles for displaying one thing or another depending on the boolean value of some variable.

    [01:42 - 01:51] Let's start with the imports. We'll need the link component from React Router so that we can navigate our users around the app when they click on a menu item so we pull that in.

    [01:52 - 02:03] We're also going to be checking if a user is authenticated or not using the custom hook use auth so we'll bring that in too. Now for the skeleton component and default export.

    [02:04 - 02:17] After calling the use auth hook here we can pull out just the is authenticated check and log out function to sign a user out of the app. With a return statement we have a HTML nav element with some Bulma classes in place that will wrap our menu items.

    [02:18 - 02:28] Let's define those next. In the top section where we have the nav bar brand class we're adding a link component around the little dinosaur logo we added to the project in a setup lesson.

    [02:29 - 02:38] This link will take our users to the home page from wherever they are in the app at the time. The nav bar burger element will display a hamburger style navigation menu on a mobile device.

    [02:39 - 02:53] I recommend naming the file cartoon dash dyno png in the setup lesson and making sure it's saved to the client public folder. However, if you have the file name something different just take care to use the correct file name in this lesson otherwise you'll see a broken image when we run the app.

    [02:54 - 03:07] In the element with the ID value main nav we have two distinct sections nav bar start and nav bar end. These are dictated by the Bulma framework with the former displaying navigation items to the left of the bar the latter displaying them to the right.

    [03:08 - 03:20] In the left hand side we have another two link components that take our users to the search page to find a dinosaur by name and the browsing page to just scroll through dinosaurs on offer. This last one doubles up as a search result page too.

    [03:21 - 03:35] In the nav bar end side of things we have a couple of checks for authenticated users going on. With the first one we're using the shortcut expression and evaluation to check if a user is authenticated and if there are then we render another link component that will take them to the favorites page.

    [03:36 - 03:42] If they're not this link won't display at all. Finally we have two statements that offer the exact opposite behaviour to each other.

    [03:43 - 03:54] The first checks for an authenticated user and if it finds one shows the button that allows a user to sign out. If they click this we use a simple inline arrow function to call the logout function that we pulled from the user off hook.

    [03:55 - 04:13] The second checks to see if the user isn't logged in and if that's the case it displays a link component that allows the user to navigate to the login page to authent icate against the API. React like JavaScript is very flexible and it will allow you to find your logic and component rendering in lots of ways to suit different according approaches and styles.

    [04:14 - 04:36] For example you could do this last section in a single line as a ternary if statement like is authenticated, question mark, render one thing, call on, render another thing. This is fine for more concise or terse code but when we're dealing with conditional display of other components and their own attributes and properties as we're doing here I prefer to have a separate and distinct lines that use the short core expression evaluation syntax as a sort of toggle.

    [04:37 - 04:44] To my eye it looks more clear and easier to read. The completed file should now look like this.

    [04:45 - 04:57] The login form is going to be a straightforward component that will accept a username and password and authenticate the user against the API. Either redirecting into the homepage upon success or displaying an error if authentication fails.

    [04:58 - 05:09] Let's bring in some imports. We'll be using the use state hook so we grab that from React and we'll need to redirect the user if they sign in successfully so we'll need the redirect component from the React root and library too.

    [05:10 - 05:22] Finally we'll be using various functions from the use off hook so let's pull that in too. Before we flesh out the main component logic let's define the skeleton body and default return.

    [05:23 - 05:33] With the skeleton in place let's put some flesh on those code bones. We want to keep track of any errors that occur and that's a perfect job for use state so we employ that in the first line.

    [05:34 - 05:39] Next we're pulling in a few items from the use off hook. It's loading which will tell us if we're doing any loading operations i.e.

    [05:40 - 05:51] things involving the API at any given moment. The user is authenticated, tells us if the current user is authenticated or not and login is the abstracted service function that will do all the talk into the API level .

    [05:52 - 06:10] We only have a single handle function here, handle form submit which does exactly what it says in the name, handles a submission event of our soon to be added html form. Inside this air sync function we stop a full page refresh with the event.pre vent default function and then reset the errors instead using the set errors false function.

    [06:11 - 06:27] Next we're awaiting the response from the login function provided by the use off hook passing in the expected values of user name and password from the synthetic event objects target property. Notice how we're passing in the form values in the form at ebt.target summeray position.value.

    [06:28 - 06:38] It's another way to grab the form field values from the submitted synthetic event object. You don't always have to hook up every form field into an object via a use state to track their values in components state.

    [06:39 - 07:01] Since we're not doing anything particularly clever or detailed here, e.g. input error checking or conditional displaying over the fields and so on, we can just afford to grab the form field values directly from the form submission event. Once we have a response from the API the code moves on and we set the value of errors instead to true or false depending on if the response has an error value, which it will if anything went wrong signing the user in.

    [07:02 - 07:13] Now we'll move on to the GSX part. Remember that things always look a little more bloated around form fields and HTML forms because there's just more markup to make things look and feel how we want.

    [07:14 - 07:25] Starting at the top we have our familiar little mascot being displayed in a figure element followed by the title of signing. After that comes the form with the handle forms submit event handle function attached to its unsubmit event.

    [07:26 - 07:40] We have a user name and password fields with only the lightest of conditional code in place around the class name attribute. In both fields we add the input CSS class and then we check the errors value instead, adding an additional is-danger CSS class if it has a value.

    [07:41 - 07:55] This will just give a nice little UI clue to the user that something's wrong if there are errors during the signing process. Note, when it comes to run the app and play around with it, remember that to simulate a successful sign in you can enter any value in the username and password fields to authenticate.

    [07:56 - 08:03] If you want to see the errors in action just leave one or both fields empty. After the form fields we have a default submit button with some Bulma styling applied.

    [08:04 - 08:17] We're adding a conditional CSS class again but this time we're checking the is- loading boolean value from the use auth hook. If we're loading then we add an is-loading CSS class to the button which will give us a little spinner icon and temporarily disable the button for us.

    [08:18 - 08:34] Finally, after the form we have a shortcut evaluation again that looks for a truthy value of errors instead and displays an error style notification message to explain to the user that there were problems signing in and to try again. The completed files should now look like this.

    [08:35 - 08:47] On to dinosearch.jsx. In many ways the dinosearch component will look very similar to the login form because at their cause there are both essentially input forms that handle submission events and then take action based on form field input.

    [08:48 - 08:57] Let's start with the imports and components scaffolding and export. Using flash here we pull in react and then a new one use history hook from react router.

    [08:58 - 09:09] This will enable us to interact with the browser history properties and functions via the react router system. Now for the skeleton component.

    [09:10 - 09:21] We've got a standard function component here that we define and export straight after. We've called the use history hook right at the top so we can use it later and next we've outlined a handle looky click and handle search submit functions.

    [09:22 - 09:37] The first will handle the click event of an "I'm feeling lucky" style button in the form we'll be building shortly whilst the second will unsurprisingly take care of any form submissions that we make. With our bare bones event handling functions in place let's add some logic to them starting with the handle looky click function.

    [09:38 - 09:51] When the user clicks the "I'm feeling lucky" button we want to bypass the form field search input and just direct the user to the browser page but supply a random alphabet ical character to the page as a URL parameter. That's what the handle looky click function will achieve.

    [09:52 - 10:05] First we define a string of alphabetical characters and then grab one randomly slashing its value in the run_child variable. We're using a common pattern of combining math.floor and math.random times the length of the particular string or array.

    [10:06 - 10:30] Once we have this random alpha character we can use history push to add the path slash browse slash whatever our random character is into the browser's history and then navigate our user over to the new page. You could also use the reactor to redirect component here but we're using the use history approach as it enables users to navigate backwards to the search page and try another search more easily and it maintains a browser history trail which some users may find useful too.

    [10:31 - 10:42] With handle search submit we have a very similar function. We stop the form from refreshing the page and then we grab the user search term from the form fields first target properly before performing the same history.push we did in the previous function.

    [10:43 - 10:53] Adding the dynamic URL path to the browser history and sending our user to the new page. All that's left is to flesh out the components JSX so let's take care of that now.

    [10:54 - 11:09] We have some Bulma styled section and flexbox columns to help keep everything centralized on the screen and you'll notice our little dyno mascot popping up again in the figure element at the top. In the form element we're attaching the handle search submit handler function to the forms on submit event.

    [11:10 - 11:27] After this we have a search box which is a text input field and then two buttons the I'm feeling lucky button which is hooked up to the handle looky click handler function and the let's search button which isn't attached to anything but will trigger the default form submission event when clicked. The completed file now looks like this.

    [11:28 - 11:34] Let's move on to creating the dyno card component. This component looks more long and complex which belies it's actually fairly straightforward nature.

    [11:35 - 11:53] It's going to be a largely presentational component that will display information about a particular dinosaur as part of a parent list component such as the dyno browser will define just after this. Open up the components dyno card.jsx file let's start with the import section and the component skeleton.

    [11:54 - 12:10] Again we'll be making use of our old friend the link component from React router so we import that as well as our shiny new use dyno service hook. In the dyno card component definition we're destructuring the incoming props object to dyno info which will match the properties of a dinosaur information object returned from the API.

    [12:11 - 12:25] With our skeleton in place let's fill out the variables and a single function. There's lots of destructuring going on here but it all starts with setting a string constant of the base URL to where we'll find our dinosaur images into image base URL.

    [12:26 - 12:33] This is the static path that our express base API exposes to the outside world. In reality it happens to be a folder full of dinosaur images.

    [12:34 - 12:53] Next we pull out the array of favorite dinosaur ID values, favorite dyno IDs from the use dynos hook as well as both the add favorite dyno and remove favorite dyno functions that will add or remove a particular ID value from our list of favorites instead. After this we have some more destructuring this time of the incoming prop value dyno info into its constituent parts.

    [12:54 - 13:11] You don't have to do this but I like to as it feels nicer to me to refer to the variables by their name directly instead of using the longer form props.dino info.time_ frame as an example. Finally we create a variable isFavourite which is just a boolean that determines if this particular dinosaur is one of our favourites or not.

    [13:12 - 13:30] It does this by checking if the array of favorite dyno IDs includes the dinosaur's current _id value. With the single function handle on favourite click we're going to first check if this dinosaur is a favourite already and if it is then we call the remove function from the use dynos hook passing along the _id value.

    [13:31 - 13:43] If not we should call the add function instead. We carry out this toggling logic here because in the UI we just want a single button that will trigger one function called regardless of whether the id needs to be added to or removed from the state.

    [13:44 - 13:50] Like anything however there are multiple ways to approach this. In the incoming JSX we could implement this same logic as follows.

    [13:51 - 14:07] We could render two buttons one at a time that conditionally display depending on the favourite status of this dinosaur each handling the add or remove. We could also have a single button like we will shortly but move this isFavour ite checking logic into an anonymous inline arrow function directly on the on click event of the button.

    [14:08 - 14:26] Something like this on click and then an anonymous arrow function which would be isFavourite, question mark, remove favourite dinosaur _id, call on add favourite dyno _id. Personally I feel the first approach is too much JSX and adds unnecessary clutter to the rendering element.

    [14:27 - 14:40] And with the second I like to keep the event handling function called simple and small choosing to abstract any looping logic or conditional checks into a separate function where possible. This also means we don't have to rummage through the JSX to keep track of what happens on click event or change event.

    [14:41 - 14:52] We can go to the function that handles it and work out the logic going on from there. Let's add the JSX and then step through some of the finer points.

    [14:53 - 15:07] This hunk of JSX renders a card style component using Bulma's card styles. It will house an image of the dyno in question and some basic details such as its name and location as well as providing the user with an option to view more details via a link in the bottom of the card.

    [15:08 - 15:20] However, this is a decent chunk of JSX to consume in one go so let's break it down into sections and run through it. In this section right at the top we define a button whose on click event calls our handle and favourite click handler function.

    [15:21 - 15:31] Outside of the button's JSX we have Bulma's approach to adding fun awesome icons to the text using the span elements. We're using the is favourite boolean again here to change its CSS value depending on the result.

    [15:32 - 15:42] One will show a filled in heart to indicate this timer is fair-rooted, the other hollow outline to indicate that it is not. In the next section we have a card image container which houses a figure element.

    [15:43 - 15:54] We're constructing a dynamic image source path here using the image base URL and image values but we're also hiding the actual image element. We're instead to apply the dinosaurs images of background using CSS styles.

    [15:55 - 16:09] This is a handy trick to know when you're dealing with a set of images with a variety of dimensions. We can't make different dimensions fit in a particular image aspect ratio but we can apply those images of background and stretch and shape them a little to cover the same space without distorting the image.

    [16:10 - 16:30] Finally, in the card content section we're wrapping a link component around the dinosaurs name and adding in some other cursory details which is the time frame it was alive in, locations it was found and the type or family it belonged to each within a accompanying icon. Right at the end of this section we're using the same link component that wraps the name but styling this one as a button to encourage the visitor to see more details.

    [16:31 - 16:42] A completed file should now look like this. We've got a lot of the building box in place now so it's time to start putting the parent components together starting with diagonal brows.

    [16:43 - 16:52] Open up the file and let's start with some imports. We're using both the use state and use effect hooks from React here as well as pulling in the use perams hook from React router.

    [16:53 - 17:00] The use perams hook will allow us to access any routing parameters in the URL. Next, we've got to use the use dinos hook from the service layer again.

    [17:01 - 17:11] Finally, we'll impart our freshly minted dino card component ready for use too. Now onto the skeleton component and default export.

    [17:12 - 17:30] We need to set up a couple of local state variables as well as access a root parameter and some functions from the use dinos hook so let's define these all. We have three variables, dino data, search term and errors that we're using the use state hook to set up.

    [17:31 - 17:41] The first will hold an array of matching dinosaur objects returned from the API whilst the second will track the current string value that we use researching on. This could be a dinosaur name or alphabetical character.

    [17:42 - 17:51] Finally, the third, errors, could hold error information should anything happen with the request to the API. After this we're extracting a name variable from the use perams hook.

    [17:52 - 18:06] If you remember back to the app component where we set up our routing information, we had a root that looked like this /brows/callonname. In this case the call on name part of the URL path will be a dynamic root parameter whose value will only know about when someone navigates to that URL.

    [18:07 - 18:22] However, although we've called it call on name here, we still need to get hold of it runtime before we can actually use it. ReactRooter wires all this up for us in the background of providers a neat little hook use perams to extract any URL root parameters just like we've done with our name variable here.

    [18:23 - 18:36] We've chosen names the moniker for this root parameter but you could have just as arbitrarily called it Batman or Dino and the routing system would pass those values along. Of course you'd then have to update the variable we have here to use those names so keep that in mind.

    [18:37 - 18:45] Oh and this also works with multiple chained root parameters too. Next we pull our is_lawding_flag and the load_dinos function from the use_dinos hook.

    [18:46 - 18:54] And our final variable is alphabet whose value is exactly as its name's name's name, the alphabet. We'll be using this in a moment when we get to the JSX.

    [18:55 - 19:02] Let's outline our function to next. The first function fetch_dinos is the main interaction point with the API.

    [19:03 - 19:11] Rather it talks to the Dino service which talk to the API for us. Initially we update the search_term value in local state using the past in term argument value.

    [19:12 - 19:21] We'll use this later on for some messaging to the user. Next it calls the load_dinos function from the Dino service waits on the response passing in any term value that's passed to it.

    [19:22 - 19:34] If there's no errors in the response object then we can update state with the response data which will be an array of dinosaur objects. If there are errors then we can set some error data instead in the set_error function.

    [19:35 - 19:47] With handle search submit we're going to take care of the search form on this page and its form submission event. We prevent the page reload and then immediately call the fetch_dinos function we just defined passing in the form search input value as we've done in previous components.

    [19:48 - 20:03] Finally we have an instance of the use_effect_hook which is passed an empty array as a dependency so we know it'll only find once on initial component mount. If users come here of their own a card without searching first then we want to list all dinosaurs so the use_effect_hook comes in handy for this very purpose.

    [20:04 - 20:22] We check to make sure we're not already loading for some reason and call the very same fetch_dinos function as we've done so far. We've got quite a lot of JSX to add to this component so let's get it down first and then walk through the key parts.

    [20:23 - 20:33] At the higher level we have a section element in a series of containing divs that are set up a flex box grid structure. In the first column lives a sidebar wrapped in a nicely styled dot box class.

    [20:34 - 20:49] What's happening here after the title search by letter is that we start with a button that directly calls the fetch_dinos function on its onclick event. This passes a null value to the function so that all dinosaurs retrieve from the API should our users wish to view them all and browse.

    [20:50 - 21:12] After that we're taking the alphabet string variable and looping through each individual character turning each one into a button that still calls the fetch_dino function but this time passing in itself i.e. that's a specific letter of the alphabet. So when we took a look at the API and the dinos roots there was one that handled dinos/search/term determining the value of term with a single character or part of a name and matching dinosaurs as appropriate.

    [21:13 - 21:27] Well here we're only going to be passing along a single character, a letter of the alphabet so what we will get back is all the dinosaurs whose names begin with whatever letter our users have clicked on. In this next section we're dealing with the larger right hand column where the results and search form will be.

    [21:28 - 21:39] We have a dynamic title assigned to the h1 element that shows a different message depending if our user has searched for something or was showing everything. Under here we have a little search form that looks very similar to the one in the dinos/search component.

    [21:40 - 21:51] We allow users to enter a search term in the h2ml input element and then trigger a form submission with a default button in there. We assign the handle search submit function we are already defined to the on submit event of the form.

    [21:52 - 22:04] In this last section we start by checking to see if we're not loading and if the dino data array instead has at least one value. If both of these conditions check out then we loop through each dinosaur object in the array and render a dino card component.

    [22:05 - 22:15] It's a neat little snippet of code but it's so powerful because it hands off rendering responsibility of the dinosaur information to the dino card component. Under here we have a couple of conditional rendering sections.

    [22:16 - 22:24] The first render is a standard h2ml progress bar if the is loading flag is set to true. It's a handy indicator to the user that something is happening and provides them with some good UX.

    [22:25 - 22:40] The second has a couple more checks but essentially if we're not loading and we don't have any dinosaurs in the array we just want to output a nice little message to tell the user that their search didn't yield any results. The completed files should now look like this.

    [22:41 - 22:51] Onto dino details dot jsx. When a user wants to know more about a particular dinosaur and read more in depth information on them they'll visit the URL pattern slash dinos slash id.

    [22:52 - 23:02] The routing system will catch this and pass them along to the dino details component we're tackling next. Open up the penultimate file dino details dot jsx and let's start filling it out.

    [23:03 - 23:15] The imports look very similar to the last component. We're bringing in some React hooks up top, the used parameters hook from React router because we'll need it to access the call on id parameter from the details URL path and the used dinos hook.

    [23:16 - 23:28] With the imports done let's flesh out the scaffolding of the base component and export. With the bare bones in place the next step is to add in some variables and handler functions.

    [23:29 - 23:40] It might look like there's a lot going on with the variables but again they look somewhat similar to the last component. We'll be referencing a dinosaur's image in this component so we outline the base URL from where we can grab it in the image base URL variable.

    [23:41 - 23:56] After this we have the same two state variables from the last component only this time dino data will be a single object instead of an array. We're using the use param hook to access the routing parameter id which will be a gooid string representing an id value of an individual dinosaur.

    [23:57 - 24:02] Next we pull out just about everything we can from the used dinos hook. We'll see how these are used as we go along.

    [24:03 - 24:18] After this we extract all of the properties of the dino data object so we can use them without having to write the longer dino data dot some property name. These properties will match up with those on each dinosaur object if you take a look in the /server/data/dinos.json file.

    [24:19 - 24:31] It's up to you how and when you do this as there's a very good argument around when does this destructuring approach start to look messy and the benefits become overshadowed by a lengthy and cluttered code file. I'd say it was skirting close to that line but we're still okay for now.

    [24:32 - 24:42] Finally we're creating a simple is favourite flag again that just checks if this particular dinosaur has already been favourited by the user or not. Next onto the functions.

    [24:43 - 24:56] A keen id individual such as yourself will know how to spide that the handle on favourite click function operates in exactly the same way as it's cousin in the dino card component. It checks to see if this dinosaur has already favourited and takes appropriate opposite action.

    [24:57 - 25:08] If you're thinking to yourself "Hey couldn't we just reduce this repetition and move the handle on favourite click to a common place for both components?" then that's excellent. This is exactly the sort of question to keep in mind as you are developing components in React.

    [25:09 - 25:19] We could move this repeated function into our dino service and update both components to use that instead. This reduces responsibilities into a single place and reduces the code base, albeit by tiny amount.

    [25:20 - 25:28] This could be a good exercise to practice with once we've finished the app. Our use of the user effect hook here ensures that we start loading a dinosaur up as the component mounts.

    [25:29 - 25:46] If we're not loading then we call our air synchronous fetch dino function that calls the load single dino function from our dino service, passing in the id value that we nab from the root parameters via the use params hook. Once the service returns to us we either update the dinosaur information instead or do some error handling.

    [25:47 - 26:01] Next up we need to outline the JSX and I must warn you it's going to look very long but a lot of it has very repetitive table data. On that point, just as with the functions above me talked about refactoring, when you see the table data in the JSX, each row is going to look largely identical.

    [26:02 - 26:18] Whilst it might seem like a bit of a drastic move to refactor each of these table rows into another component, it's worth considering. It'll reduce the complexity of the JSX in this component, make things easier to test, and mean that if you want to make any changes you'll only have to do this in one place rather than in every single row we have here.

    [26:19 - 26:37] Starting at the top we have another section container and flexbox column structure that's populated with h1 and h2 elements that contain the dinosaur's name and pronunciation. Under here we have an image for the dinosaur that looks and works like we've used in a couple of components already, and the next section handles the favouriteing and unfavouriting of this dinosaur.

    [26:38 - 26:47] It functions in a similar fashion to the same mechanism in the DINO card component. On button click we call the handle on favourite click function which will toggle the favourite stairs in app state.

    [26:48 - 26:57] We use the is favourite flag a couple of times, one to surrender the correct action message to the user, the other to render the correct icon, i.e. Filling heart or outlined heart.

    [26:58 - 27:08] Now we get to the long part. If you break it down however you can see that we've really just got a plain vanilla hml table with 6 rows, one for each of the general dinosaur properties.

    [27:09 - 27:25] Each row has a property name such as length or diet, a matching icon and the value for that property from the dinosaur information we grabbed from the API on component mount. After the table we're simply rendering the description of the dinosaur or a "no information" available message to the user if there isn't one to display.

    [27:26 - 27:38] The completed files should now look like this. We've built out a lot of services, helpers and components but you'll be glad to hear that your coding masterpiece is almost complete.

    [27:39 - 27:51] We have just one final component, favourites.jsx to put together and then we can run the finished app and have an explore. Open up the components, favourites.jsx file and let's start with those in parts .

    [27:52 - 28:07] You'll see a familiar set of in parts here as in the last few components we've built. We're bringing in React and some built in hooks, then we have the used dino service hook and we're also imparting the dino card component again as our favourites will look very similar to the list of dinosaur results displayed in the browsing and searching route.

    [28:08 - 28:22] The only additional in part here is the with authentication required component that we put together in the previous lesson as we built out the dino service. We can see exactly how it's used now as we outline the component skeleton.

    [28:23 - 28:29] You can see that the component definition isn't any different to that which we 've created previously. However, the export part looks much different.

    [28:30 - 28:47] Instead of simply exporting the favourites component as you'd expect, we're exporting the with authentication required component which you'll recall is a higher order component i.e. a function that returns a function. In our case with authentication required is a function component that returns another function component, favourites, which we're passing in as the first parameter.

    [28:48 - 29:01] Or, if the user isn't authenticated, a React router redirect component that will whisk our users off to the login page to authenticate first. The second bit of information we're passing in is an object with the location property whose value is set to the favourites route.

    [29:02 - 29:15] The with authentication required component passes the location along to the redirect component which in turn passes the information along to the login form component. At this point it would typically be used to redirect the user back to where they were before being asked to authenticate again.

    [29:16 - 29:26] However, we've chosen not to add some more complexity here to achieve that aim as the added complexity outweighs the benefits to our app. This different look and default export shouldn't look too weird to your eyes though.

    [29:27 - 29:38] During the module on Redux we use this idea of a higher order component wrapping an export when we use React Redux's connect function. With our skeleton done let's map out the variables for this component and a couple of functions.

    [29:39 - 29:52] I'm sure you'll be sensing a pattern here but the variables are very similar to those of the previous components. We'll be using DynoDator to store a list of returned dinosaur objects from the API in local state and we've initialized this value with an empty array.

    [29:53 - 30:07] The errors variable will hold error data and we're extracting some of the functions and values from the used Dynos hook, specifically a loading flag, a list of dinosaur ID values that we have favourited and a function to load all Dynosols from the API. Next up we have the fetchDynos function.

    [30:08 - 30:16] This is pretty much the same function as the one in previous components with the same name. We are aware to response from the services loadDynos function which talk to the API.

    [30:17 - 30:26] Once we have a response we check for errors and save our list of Dynos instead if we're all clear. If not then we update the errors boolean so we might potentially use it later on.

    [30:27 - 30:38] Note that we've left this hanging term argument in the fetchDynos function. In truth we won't be populating this with a value at any point during this component but we can leave it here because the loadDynos function cares for a null or empty value.

    [30:39 - 30:57] Overplanning and future proofing of code can be just as bad as not planning for things at all but it's feasible that over time the list of favourites might grow so that our users want to filter them by a category or alpha character. At this point being able to pass a term value would be useful and it's such a small trivial bit of additional code that we can safely leave it here even if we're not going to use it right now.

    [30:58 - 31:15] Our use of the use effect hook here is quite small and we can ensure that it only runs once on component mounted by passing in an empty array as the load dependency. It simply calls the fetchDynos function passing in a null value for the term parameter to leave no doubt that what we want is a list of all Dynosols please.

    [31:16 - 31:25] Now onto the JSX. Thankfully this component's JSX isn't as long or intricate as some of the other components we've coded.

    [31:26 - 31:39] We define another section for padding and spacing and then add a helpful H1 element with a title. After this we have a block of logic that first checks to make sure that 1) we 're not loading and 2) we have some objects in a DynoDearToArray.

    [31:40 - 31:58] If both conditions are true then we're running the filter function on our DynoD earToArray taking only those Dynosols objects whose ID value matches any ID in the favorite DynoDear array held in the Redux store. The filter function returns as a new array of our filtered data which we can then chain with a call to the .map function.

    [31:59 - 32:15] Inside of the map function we render a one third width column that houses an instance of the DynoCard component. You can see that we're passing the Dyno value which represents a single Dynosol information object from our filtered array into the DynoCard component so that it can do its excellent job of displaying this information.

    [32:16 - 32:20] If you're asking yourself why can't we just leave things at the filtered stage? Why do we need the map too?

    [32:21 - 32:24] That's a great question. React components must return JSX.

    [32:25 - 32:42] You can return other components and you can perform logic using double-braced syntax as we've done here but ultimately any result of logic performed during the rendering power of a component should return JSX. If we stopped at the filter point we'd absolutely have all the data we need but React will throw an error that looks like error objects are not valid as a React child.

    [32:43 - 32:48] Now that's exactly what would be happening. We'd be trying to render an array of Dynosol objects in their raw form.

    [32:49 - 33:05] We need the additional map function to further process our filtered data returning another new array but this one full of actual JSX that React can understand and will happily render for us. Notice too how we're adding a key attribute which is important when rendering loops of things so that React can keep track of them in the virtual DOM.

    [33:06 - 33:23] It's not absolutely necessary and it shouldn't break your code but React will complain to you about it and there's always a chance that you might experience unwanted results or strange behaviour. It's best to get into the habit of applying the key attribute whenever you're inside of a loop that renders components on JSX, passing each of them a unique item value.

    [33:24 - 33:35] In our case since the Dynosol's underscour ID values are all gooids, we can use this. On client side filtering, you might notice that we're asking the API for all the Dynosol's and then doing the filtering on the client side.

    [33:36 - 33:48] This sort of situation always comes down to personal and project choice and technical needs. In smaller projects with smaller data sets like we have here, there's absolutely no problem fetching all of the data and doing some local filtering sorting and even page in.

    [33:49 - 33:55] Believe me the difference in shifting the sorting and filtering to the server side, i.e. the API, will be some minimal, it will be insignificant.

    [33:56 - 34:17] However, once your app starts to scale and the data involved becomes very large and it makes sense to shift to a server side model where the intensive data operations such as filtering, page in and sorting and so on can be carried out by a more powerful set of resources. Again, like we've mentioned here, you don't need to start over optimizing too early and with too much gusto, but this sort of thing is always worth having in the back of your mind as you scale up your apps.

    [34:18 - 34:34] In the final section we have two conditional expressions that may look familiar from previous components. The first checks the is loading state and renders a progress bar if we are in fact loading and the second checks to see if we're not loading and if we have either zero Dynosol results from the API on no Dynosol is in our favorites list.

    [34:35 - 34:47] If either of those conditions are true then we can present the user with a little convenience message that they haven't made any favorites just yet and to encourage them to do so. The completed files should now look like this.

    [34:48 - 34:59] We've covered an awful lot of ground in this lesson and we're finally ready to fire up the coding machine and run our app. However, we're going to push paths for a moment and collect ourselves, picking things back up in the next lesson where we'll make sure everything's in order.