This video is available to students only

Building the components

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

Fleshing 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.

In this lesson we’re going to work through the components we defined right at the start of the module and craft our UI, which will be powered by the hard work we completed in the last lesson.

Let’s start with the Layout.jsx component.

Layout.jsx#

The Layout component is simple and presentational 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. Now’s the time to put those common elements in place and we’re going to define the entire file here in one go since it’s quite small:

We pull in the Navigation component (which we’ll build out next) and then define and export the Layout component. We’re destructuring the incoming props argument passed to the component, pulling out the children value. The children property is one of a few default properties on each props object passed to a given component. 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 semantic <section/> element and child container with some scant Bulma classes on them to just give some padding and other spacing.

Because this component will be applied to any route that’s rendered by 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.

Imports section#

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. We’re also going to be checking if a user is authenticated or not using our custom Hook, useAuth so we’ll bring that in too.

Now for the skeleton component body and default export:

After calling the useAuth() Hook here, we can pull out just the isAuthenticated check and the logout function to sign a user out of the app. With the return statement we have an HTML <nav /> element with some Bulma classes in place that will wrap our menu items.

Let’s define those next.

Component body#

In the top section where we have the navbar-brand class, we’re adding a Link component around the little dinosaur logo we added to the project in the setup lesson. The link will take our users to the home page from wherever they are in the app at the time. The navbar-burger element will display a hamburger-style navigation menu on a mobile device.

I recommended naming the file cartoon-dino.png in the setup lesson and making sure it was saved to the /client/public/ folder. However, if you have the file named something different, just take care to use the correct filename in this lesson otherwise you’ll see a broken image when we run the app.

In the element with the id main-nav we have two distinct sections, navbar-start and navbar-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.

In the left-hand side we have another two Link components to take our users to the search page to find a dinosaur by name, or the browsing page to just scroll through the dinosaurs on offer — this one doubling up as a search results page too.

In the navbar-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 evaluation to check if the user is authenticated and if they are then we render another Link component that will take them to the favorites page. If they’re not then this link won’t display at all.

Finally we have two statements that offer the exact opposite behavior to each other. The first checks for an authenticated user and if it finds one, shows the button that allows that user to sign out. If they click this, we use a simple inline arrow function to call the logout function we pulled from the useAuth Hook.

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 authenticate against the API.

React, like JavaScript, is very flexible and will allow you to define your logic and component rendering in lots of ways to suit different coding approaches and styles. For example, you could do this last section in a single line as a ternary if statement, like isAuthenticated ? 'render one thing' : 'render another thing'. This is fine for more concise or terse code, but when dealing with the conditional display of other components with their own attributes and properties as we are doing here, I prefer to have separate and distinct lines that use the shortcut expression evaluation syntax as a sort of toggle. To my eye it looks clearer and easier to read.

The complete file#

The completed file should now look like this:

LoginForm.jsx#

The login form is going to be a straightforward component form that will accept a username and password and authenticate the user against the API, either redirecting them to the home page upon success, or displaying an error if authentication fails.

Let’s bring in some imports:

We’ll be using the useState 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 React Router too. Finally, we’ll be using various functions from the useAuth 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:

Variables and logic functions#

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 useState so we employ that in the first line. Next, we’re pulling a few items from the useAuth() Hook:

  • isLoading will tell us if we’re doing any loading operations (i.e. things involving the API) at any given moment.

  • isAuthenticated tells us if the current user is authenticated or not.

  • login is the abstracted service function that will do all the talking at the API level.

We only have a single handler function here, handleFormSubmit, which does what it says in the name: handles the submission event of our soon-to-be-added HTML form.

Inside this async function we stop a full-page refresh with evt.preventDefault(), then reset the errors in state using the setErrors(false) function. Next, we’re awaiting the response from the login function provided by the useAuth Hook, passing in the expected values of username and password from the synthetic event object’s target property.

Notice how we’re passing in the form values in the format evt.target[0].value? It’s another way to grab form field values from the submitted synthetic event object. You don’t always have to hook up every form field into an object via useState to track their values in component state. Since we’re not doing anything particularly clever or detailed here (e.g. input error checking or conditional displaying of other fields, and so on), so we can afford to just 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 in state to true or false depending whether the response has an error value (which it will if anything went wrong signing the user in).

Component body#

Now we’ll move on to the main JSX part:

Remember things always look more bloated around form fields and HTML forms because there’s just more markup to make things look and feel how we want. Starting at the top, we have our familiar little mascot being displayed in a figure element, followed by a title of ‘Sign in’.

After that comes our form with the handleFormSubmit event handler function attached to its onSubmit event. We have username and password form fields with only the lightest of conditional logic in place around the className attribute. In both fields, we add the input CSS class, then check the errors value in state, adding an additional is-danger CSS class if it has a value. This will just give a nice little UI clue to the user that something’s wrong if there are errors during the sign-in process.

Note, when we come 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. If you want to see the errors in action, just leave one or both of the fields empty.

After the form fields we have a default submit button with some Bulma button styling. We’re adding a conditional CSS class again, but this time we’re checking the isLoading boolean value from the useAuth 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.

Finally, after the form, we have a shortcut evaluation again that looks for a truthy value of errors in state and displays an error-styled notification message to explain to the user that there were problems signing in and to try again.

The complete file#

The completed file should now look like this: