Using React Router v4 with create-react-app

Introduction – react-router

React-router is the most popular routing solution for applications made in React. It is a collection of navigational components that compose declaratively with your application. Bookmarkable URLs, history, customizable navigation links and quite a few other features come with it.

I had a need to learn more about react-router v4 so I decided to share my notes and turn them into a blog post. Some of the stuff will be straight from the documentation, so, bear with me. The post will also contain practical examples and a link to GitHub repository where you can find the history of commits for the simple application built with create-react-app and react-router.

In this post, we will focus on using React Router version 4, since that is the current major version. With version 4 react-router has been split into three packages:

  • react-router
  • react-router-dom
  • react-router-native

The core features have been split into the react-router package. Hence, both react-router-dom and react-router-native depend on that package. Since we are making a web application we will use react-router-dom package. In almost all cases, you will want to use either react-router-dom package or react-router-native package, never react-router alone. 

 

React-router key concepts and components

Lets first get a grasp of basic concepts regarding react-router, we will not dive into these details right away, instead, we will just list basic concepts and see how they’re useful. Later on, we will try to see their practical usage in form of examples.

Router

This is one of the core things, obviously. It is also a common low-level interface for all router components:

  • BrowserRouter
  • HashRouter
  • MemoryRouter
  • NativeRouter
  • StaticRouter

The Router also has history object to track the navigation.

Route – <Route>

Another core thing and also the most used component in React Router.

Route renders your UI when a location matches the route’s path.

There are 3 ways to render something with Route component:

  1. component attribute – component>
  2. render attribute – render>
  3. children attribute – <Route chidlren>

More about these 3 ways later on. However, all of these 3 ways of rendering UI with Route component will get same props:

  • match
  • location
  • history

match

A match object contains information about how a path attribute of Route component matched the URL.

It contains the following information:

  • params – (object) Key/value pairs parsed from the URL corresponding to the dynamic segments of the path
  • isExact – (boolean) true if the entire URL was matched (no trailing characters)
  • path – (string) The path pattern used to match. Useful for building nested <Route>s
  • url – (string) The matched portion of the URL. Useful for building nested <Link>s

location

Locations represent where the app is now, where you want it to go, or even where it was.  A location object is never mutated so you can use it in the lifecycle hooks to determine when navigation happens, this is really useful for data fetching and animation.

history

This is huge. This one is a major concept and also it is a separate npm package and react-router‘s only major dependency (besides React). It provides different implementations for managing session history in JavaScript, in various environments. The history object is mutable. More about that later on.

The following terms are also used:

  • browser history – A DOM-specific implementation, useful in web browsers that support the HTML5 history API
  • hash history – A DOM-specific implementation for legacy web browsers
  • memory history – An in-memory history implementation, useful in testing and non-DOM environments like React Native

Each history object has the following properties:

  • history.length – The number of entries in the history stack
  • history.location – The current location
  • history.action – The current navigation action

Redirect – <Redirect>

This is another important component – <Redirect>. Rendering this component will navigate to a new location. The new location will override the current one in the history stack.

import { Route, Redirect } from 'react-router'

<Route exact path="/" render={() => (
  loggedIn ? (
    <Redirect to="/dashboard"/>
  ) : (
    <PublicHomePage/>
  )
)}/>

 

 

Start with simple application using create-react-app

Let’s start by creating a new React app with the create-react-app tool:

Next thing is to add the react-router-dom package:

Adding routes – rendering UI

Let’s add some routes.

First, let’s make an additional stateless component named Home that will simply show the Home header.

As you can see, we also simplified App component by turning it into a stateless functional component, dropping the need for class, since we will not use the state for that component. At least not yet.

#1 Render option – using component attribute

Now we can use the Home component for testing out a simple route. So, let’s create a route for Home component.

Let’s add another route called Admin:

Do notice that I added div as child Router component. That’s due to Router‘s limitation to have only one child. For us to have multiple <Route> components inside of <Router> we need to wrap Route components with div or another element/component.

If we now go to http://localhost:3000/Admin location, you will see Admin header on the page. However, do notice it’s also showing the Home header. That’s because both of the routes include “/” route and they are both matches. The slash is the problem.

React-router has routes as inclusive by default, meaning that it can match and render multiple <Route> components. If we wanted just one route to ever match in a group of Route components we would use Switch component, but we will not talk about that right now.

Using exact attribute

We can use the exact attribute for Route component that will solve this problem. So, let’s add the exact attribute to the Home route.

If you now go to /admin route it will show only Admin header. If we go back to http://localhost:3000 it will show the Home header. 

The strict attribute

However, if you change your Route path to ‘/admin/’ and go to http://localhost:3000/admin the /admin/ route will catch it and you maybe don’t want that. In that case, you can use strict attribute. Here is a brief info about strict, from the documentation:

When true, a path that has a trailing slash will only match a location.pathname with a trailing slash. This has no effect when there are additional URL segments in the location.pathname.

We can simply add strict attribute to /admin/ route

Now, only when you go to /admin/ route the Admin component will render.

#2 Render option – using render attribute

Let’s add another route. However, this time we will not create a separate component for that route. Instead, we will use the render attribute available on Route component.

The name doesn’t really matter, let’s make it “/logs“. We will simply use a render attribute and assign it a value of fat arrow. Then we return some HTML from that fat arrow method.

#3 Render option – using children attribute

There is another way to render your UI stuff based on the route. And that is via children prop. It is almost the same as for render option, the only difference is that children will render your UI regardless of the match.

It is pretty useful when you always want to render a component or piece of UI, regardless of the match.

However, we can change the logic when it should match and render UI data. As we said previously, all three options of rendering UI for the route (component, render, children) receive same props: match, location and history.

This will print out three props: history, location and match. The match will be undefined if there is no match.

Let’s make use of that!

The header will now be rendered only when there is a match. Otherwise, method inside of children will return null.

You probably got tired of entering full paths and routes manually, so let’s see how to add links to the routes!

 

Using Link component

First, we need to import Link component from our package – react-router-dom:

Now, let’s create a simple NavBar component:

We can now add this component to our header. However, it needs to be placed inside of the <Router> component. If we now go to our application, we will see two links to Home and Admin routes.

Links are used to lead to locations(not to actual components), then routes will render whatever we configured them to render. That can be either a component or simple HTML inside of render prop.

Content inside of to prop has to be a string, which is consisting of the location’s pathname, search, and hash properties.

 

Using to as an object

We can use to prop of Link component as an object as well. This object can have any of the following properties:

  • pathname – string for the path to link to
  • search – string for query parameters
  • hash – a hash to put in the URL
  • state: state to persist to the location

 

For example, we can add a Link to our logs location:

Using replace prop on Link

We can use replace prop to replace the last (current) entry in the history stack, instead of adding a new one.

For example, if we add another Link for children route:

If we now go to Home route and then to logs and then to Children route, and we go back, it will lead us directly to Home instead of to logs. Browser history was changed. The last record was overridden/replaced.

Using <NavLink>

NavLink extends Link component. It enables us to style the output if it the route matches the current URL. There are two ways to style the rendered output:

  • activeStyle (object)
  • activeClassName (string)

It also has three additional props which can affect the matching:

  • exact (bool)
  • strict (bool)
  • isActive (function)

 

I will add activeClassName prop to the first Link and assign it value of ‘active’:

Let’s style our css to match the active class with hrefs:

Once you go to to Home route you will notice the link to Home is now blue.

 GitHub repository

You can find the GitHub repository (work in progress) here.

Summary

React Router v4 has been redesigned and common features and code have been separated out to the react-router package. We have two additional packages, that are usually used: react-router-dom and react-router-native. We saw some of the basic concepts and components of React Router. Also, we showed usage of-of the react-router-dom package.

In the next post, we will talk about URL and Query Parameters, rendering routes, route redirects and handling route changes to do some actions.

 

 

Ibrahim Šuta

Software Consultant interested and specialising in ASP.NET Core, C#, JavaScript, Angular, React.js.