React Router Dom — The basics & set up for potential JWT authentication.
Let’s start with a basic React App install with react-router-dom.
npx create-react-app my-app
Once we are inside the application, let’s install react-router.
npm install react-router-dom
Before we dig in, let’s first discuss what we will be doing today. In my experience, if we are building a slightly bigger application and want to implement client-side routing along with user authentication like a JSON Web Token it is important to get started with the right foot. That way we can avoid any mishaps and confusion down the road. This is what we will be covering today, setting up our routes early on and that will give us solid groundwork to build out more features/components/etc. This blog will not be covering JWT implementation, rather just checking if the JWT is valid or not. It’s just one way of setting up your application that will prevent anyone directly using routes to access part of the app without being properly authenticated. Below is the outcome of our basic app:
It is important to note that React creates the basic application using functional components, in this blog we will be using class components. If you do not convert the components, this will not work.
First thing that we want to do is modify our index.js file that is housed in the src folder. We want to import BrowserRouter and encapsulate our <App /> tag. This will provide us with the ability to use routes for different components. The way react-router-dom works is that the routes are passed into the history object. Without access to this.props.history we will not be able to reroute our application.
Next, we want to jump into our App.js. Now remember, React will create this as a functional component, we need to convert it to a class component. This file will serve as a “route controller” for our application. All the components that our application has, will be routed through this file. I created a folder called “components” inside the src folder to house all components that our application will have. As we saw in the GIF above, we will be working with three components. App.js is where we import two more tags from ‘react-router-dom’, the Route and Switch. These tags will provide access to the history object I mentioned above to our children components. We will also need to import our three components here that we want to display when we hit a particular route. Our finished code will look like this:
Each route inside the Route tag has an “exact” attribute which tells our application that only one component can be displayed at one time.
Lastly, we need to add a way for our each child component that we see to redirect us to the next component. We use this command:
This command can be used on an event listener or any function that you would like to redirect your user. Here is what our child component looks like:
If you do not want to explicity list a route that you want your user to hit, for example a Back Button, you can use the goBack() function. It is used like this:
‘props’ of undefined
Without access to the history prop we would not be able to redirect our user to the next component. If you are ever in a situation where a particular component does not have access to the history object, you will get an error similar to this:
TypeError: Cannot read property ‘props’ of undefined
You may want to try providing direct access using withRouter.
By importing withRouter from ‘react-router-dom’ we provide direct access for the component to use the history object. The process of implementing this is relatively straight forward. First we need to import it, then we need to export the component along withRouter. For example, if our component One did not have access to history and we wanted to use withRouter, it would look like this:
This is a basic layout of how we can use React-Router-Dom. Now let’s take a look where we would add the authentication process.
Authentication & componentDidMount
If we did not want to allow the user to access component 2 and 3 unless they properly signed in from component 1, we would need to verify the authentication somewhere. For this example we will be using JWT. Once a user is signed in, we would assign them a JWT to their browser. The application would need to check if the token is valid when the user comes back to the app, but where do we check for the token? The answer is our App.js file where we control the traffic. We will be using a life-cycle method called: componentDidMount.
For the purpose of this demo, let’s treat component 1 as the sign in page and component 2 and 3 as the core application with user information.
If the token is valid and therefore holding a truthy value, we can allow the application to redirect the user past the sign in page and into the application itself. If the token is not valid, falsey, we want to redirect the user to the sign in page even if they try to directly route themselves by tying in the URL, which in this case would be localhost:3000/2 or localhost:3000/3.
The final code would look like this except the token value would not be hardcoded:
Each time the user interacts with the application, App.js will validate the token. If validation is failed, they will be redirected to the sign in page but if validation is passed, we can do anything you see fit for your application. If you are using JWT authentication then the else statement would not redirect the user to another component but instead would fetch the user information from the backend and render your application.
The benefit of this set up is that if you have a large amount of components in your application, you do not need to validate each one individually. This componentDidMount will be triggered with most redirects and will capture refreshes as well.
I hope you found this blog useful! If you would like to take a look at deeper implementation of this process in my actual application with Redux, take a look at my React Repo.