Frontend Navigation with React Router

undraw svg category

Welcome to Part 14 of Up and Running with FastAPI. If you missed part 13, you can find it here .

This series is focused on building a full-stack application with the FastAPI framework. The app allows users to post requests to have their residence cleaned, and other users can select a cleaning project for a given hourly rate.

Up And Running With FastAPI

In the last post, we bootstrapped our React application using create-react-app and built out a navbar and landing page with the help of elastic-ui and styled-components. We also built a custom useCarousel hook that we coupled with framer-motion to power two animations on our landing page.

Now we’ll leverage the infrastucture we’ve developed to implement routing and build out login, sign up, and profile pages. To do all that, we’ll bring in the react-router library.

The logical place to start is with routing, so let’s do that.

In a traditional application, routing happens on the server. Users change the url and the server provides some html and css that matches the new route. React doesn’t work like that. Everything goes down in the browser and requests to the server only happen when the app makes them deliberately. React Router provides a set of navigational components that sync up with the browser’s url and handle routing for us.

In the previous post, we installed the history package and react-router-dom@next.

Let’s check out our package.json file. What was that @next thing and why did we do it?

Well, react router is in a transition period. There are two primary routing libraries - react router and reach router - both of which are merging into react router 6.0. However, it’s not done yet, so at the time this class is being taught, the 6.0.0 version is still in beta.

Personally, I’ve used lots of react router versions and the current reach router version - and I prefer reach router. I’m happy they’re unifying, and I think it’s a good direction for the community. However, when you build more react apps in the future, you’re going to want to see if any breaking changes have been made to react router. You probably won’t have to include that little @next thing either, as v6.0.0 will be fully stable soon enough.

Getting started with react-router is actually really simple. Since we’re using the @beta 6.0.0 version, the docs aren’t located on their home page. By the time you read this article, it’s very possible they’ll be front and center on react router’s site. Until then, reference this guide . If you do, you’ll see that the first step is simply importing the necessary components from react-router-dom and nesting them to declare your routing structure.

Here’s how ours will look.

Start by importing a few things from React Router.

App.js

And… tada! We’re now rendering the Landing page at our root route. Believe it or not, that’s all we need to get routing working in our app. For any new route, we add the component as a child of Routes, which in turn is a child of BrowserRouter somewhere in the tree. Each route has a path and an element prop. The path is the url and the element is the component to render at that url.

What if we were to change the url? If we navigate to something like /nothing, we see the Navbar component and, well, nothing!

This is because our Layout component is rendering children inside the StyledMain component, and rendering the Navbar regardless of whether or not it has children. So any route we navigate to will have the same Navbar component throughout our app, and the element will be rendered as a child of StyledMain.

To see real navigation in action, we’ll need to add a few more components.

Go ahead and make components for a login page, a registration page, and a profile page. We’ll then make sure to assign each othem their own path. While we’re at it, we might as well create a 404 not found page as well.

Then, add some simple markup just so that we can differentiate between each component.

We’ll export all of them and add them to our router.

components/index.js

And back in our App.js component.

App.js

Try changing the browser’s url now. Any route that matches what we have nested in the Routes component ("/", "/login", "/profile", and "/registration") will render the correct screen. Anything else will match our NotFoundPage component. That’s what the wildcard - "*" - path does for us.

Ok, but we don’t expect users to type the url they’re looking for in the browser each time they want to navigate. Instead, we use a react-router component called Link.

Head into the Navbar component and make the following updates:

Navbar.js

Pass the desired path in the to prop of the Link component and it will programmatically navigate to the intended location on click, just like a normal link.

Now, if we go to our landing page and click the login icon, we’re taken to the login page. That’s exactly what we want.

Check it out on Code Sandbox

phresh-frontend-part-2-react-router

Time to create our auth UI.

Setting Up A Login Form

We won’t do anything too wild for our login page. Users should be able to enter in their email and password, click submit, and authentication. We’ll also provide a link to navigate to the registration page if need be.

Update the LoginPage with the following code:

LoginPage.js

Now let’s build a LoginForm component that will manage user inputs. It’s a good idea to read the form guidelines that elastic-ui provides here , as they do a good job of outlining the thinking behind how we’ll structure form components.

Then go ahead and create the new component.

Add a simple component skeleton:

LoginForm.js

Export it from the components/index.js file:

components/index.js

And add it to our LoginPage component:

LoginPage.js

Ok, we have soemthing on the screen. Lovely.

Now let’s add some elastic-ui form elements to our LoginForm component.

LoginForm.js

Quite a few things going on here, so let’s break it down.

This actually works pretty well, though the form could benefit from a few improvements.

Try hitting the submit button and check the console. We see a nice message telling us the contents of the inputs when submitted.

Notice how the user is able to submit the form with nothing entered in either input field? We should prevent that. There’s also no client-side validation for properly-formatted emails or appropriate password length.

Fortunately, elastic-ui makes that rather trivial.

We’ll start by creating a new file in the utils repo called validation.js.

Then we’ll add two new validation functions for email and password inputs.

utils/validation.js

We export them as an object that matches the name of their input field. This will make it easy to do validation whenever the input value changes.

Let’s see that in action.

LoginForm.js

First, we import our validation object at the top of the LoginForm component. We also add an errors object to our state. Then we update our handleInputChange function so that the input label is used to access the appropriate validation function, and call it on the input value. If the validation function returns false, meaning the input is not valid, then we set an error in state.

By the way, some readers may be unfamiliar with the optional chaining operator, like the one seen in validation?.[label]. It works identically to traditional object value access except that if any value in the chain doesn’t exist, it shortcuts to undefined. It prevents a lot of obj && obj.val && obj.val.method() code and is my preferred way to access nested objects.

Our EuiForm and both EuiFormRow components accept an isInvalid prop that indicates an error exists. We pass that isInvalid prop the appropriate form state, cast into a boolean, along with an error prop - which is the actual error text to be displayed. We also provide the isInvalid prop to the EuiFieldText and EuiFieldPassword components for maximum user feedback.

Try submitting with empty fields now. See that nice invalid popup? Try submitting an invalid email or a password that is too short. Nothing is logged to the console and we continue to get the invalid popup. That’s a fantastic feature that comes baked into elastic-ui. There’s more that can be done here, so it’s recommended to check out their validation docs .

The result of our work is a clean, intuitive, high-quality login form that we’ll steal from any time we want to create a new form.

Check it out on Code Sandbox

phresh-frontend-part-2-creating-a-login-form-with-elastic-ui

Looking good! Onto the registration page.

Creating the Signup Page

The nice part about doing all that hard work with the LoginForm is that creating a RegistrationForm is easy. We’ll take advantage of all the code we’ve already written, since some of it will be identical. As an exercise for the reader, try to implement a RegistrationForm component independently, using the same techniques that we just went over.

Start off by copying the same layout from LoginPage into RegistrationPage and replace the LoginForm with a newly created RegistrationForm.js file.

Then update the RegistrationForm with the required fields. Make sure to include a username field, a passwordConfirm field, and a checkbox indicating the user has agreed to our terms and conditions. Oh, and an additional validation function for the username field! Don’t be afraid to reference the elastic-ui docs for these tasks.

When finished, return here for a straightforward solution.

… done?

Great. On to the code.

Designing a Registration Form

As always, create the new component.

Let’s also makes sure to export that file - even though it’s empty.

components/index.js

Go ahead and update the RegistrationPage with code stolen directly from the LoginForm component.

RegistrationPage.js

Nothing new here. Same pattern as before.

We’re ready to take a look at our solution. Building the RegistrationForm is half copy/paste, half elastic-ui docs - and that is fine with me.

Here’s what we have in the new file:

RegistrationForm.js

Not terrible, right? The big new additions are as follows:

All we really need to do now is add our new validation function.

utils/validation.js

We copy the regex that our pydantic user models employ, and test the username text against it. Once we export that function as the username property, it can be consumed properly by our RegistrationForm component.

Everything else is pretty much the same, so we won’t touch on it too much here.

Check it out on Code Sandbox

phresh-frontend-part-2-adding-an-elastic-ui-registration-page

Once the user is authenticated, they should be able to navigate to their own profile page.

Since it probably doesn’t make sense to do that until we’ve implemented an actual authentication mechanism, let’s hold off until the next post.

Wrapping Up and Resources

We’ve set ourselves up nicely here. Our frontend has pages for logging in and signing up, and all we need to do is wire them up to our backend. That’s what we’ll do next time, and we’ll bring in the redux and axios libraries to make it happen.

Tags:

Previous Post undraw svg category

Phresh Frontend - Bootstrapping A React App

Next Post undraw svg category

Managing Auth State With Redux