One Router to Rule Them All

Mat Warger
7 min readFeb 24, 2018

--

The code for this walk-through is available here.

Note: I will be assuming some familiarity with React in general, including life-cycle hooks and props/state. If you need a refresher, checkout this great article here and the official React docs.

An Introduction to React Router

Previously, we looked at a very basic example of how one can benefit greatly by using community projects such as Formik to avoid the tedium of certain solutions while embracing convention to create composable and scalable applications. We will be building on that foundation to explore the objectively great library that is React Router.

React Router has been at the forefront of routing in the React ecosystem for as long as I can remember. If you’re new to React, this is the way to go when you move beyond state and start needing more options such as parameterized routing, nesting, and conditional rendering. If you have experience with React, this brings a powerful pattern to bear in that everything is a component. It takes the composablity of React and uses that to its benefit, handling any and all use-cases with relative ease.

State Be Gone!

If you would like to follow along, please clone the Forms Demo Application. This will be the baseline upon which all examples will be drawn.

To begin, we need to look at a simple way that many applications start out: returning UI components based on props and state. This is the foundation of React, and so happens to be all that was necessary to get our previous application up and running. Our list of games is fine for what it is, and searching works for us, but really, it might be nice if we could get a little more out of it - maybe a nice look at details for each game and a way to bookmark our favorites while we're at it?

Take a look at the render method in App.js.

We’re changing the form type based on state. As the comment notes:

// this will do in a pinch, but should really be a router instead

Let’s make that happen.

Routes

The first thing we need to do is install react-router-dom. This is the package that gives us everything we need for Routes and Links in the browser. We can accomplish this using yarn.

yarn add react-router-dom

Next we need to import the BrowserRouter and Route dependencies. At the top of the file, add the following import statement.

import { BrowserRouter as Router, Route } from 'react-router-dom'

Quick tip: The as keyword can be used to rename imports for convenience.

Next, let’s add the Routes we need to display our forms.

Remove the switch statement in the render method and remove all references to the form and formType variables. Replace the form div after the header with our new Router setup (and to clean things up just a little bit, I've extracted the Header into a functional component). The components should now look like this.

Let’s breakdown a few of these elements.

The Router block is what allows react-router to match each of the Route objects path props and display the corresponding component. These can be any React component. You can also pass a function to the children or render props that returns a component, as is done for the root ("/") Route. Try going to http://localhost:3000/simple to see the router in action!

There’s our gamelist rendered with the Route since the path matches. Huzzah! But, there's one thing amiss here, and that is the "/" route and the "/simple" routes are both rendering. The matching expression still works for both of them, so it's rendering them in the order it discovers them. We would rather it be one or the other, so we can add the exact prop to the root Route to fix the behavior.

That’s more like it!

Links

Now, if you click around you’ll notice that the navigation we had setup previously no longer works and the app will yell at you since we have gotten rid of the handleChangeForm functions. Not to worry, Link is here for you.

First, we will import the Link from react-router-dom.

import { BrowserRouter as Router, Route, Link } from 'react-router-dom'

Next, we will replace the spans in the header with Link components, keeping the styling we had previously.

That’s it! Click your beautiful links and see the router do its thing.

For one last flourish, let’s give a little color to the active link so we know which form we’ve selected. This can be done with the NavLink component from react-router-dom. Replace the Link instances with NavLink. NavLink will allow us to apply styles when the route matches the Link. There are two ways we can accomplish this - either with activeStyle or activeClassName props. For this simple example, we will supply a style object that will be applied for each of our links.

Create a const above the Header declaration to hold our active style color:

const activeStyle = { color: '#61dafb' }

Next, we can supply that to the activeStyle prop on our NavLink components, as shown here:

Our active link is highlighted with our nice blue color.

Attention to Detail

Now let’s see about grabbing some details for our games. We’re going to use the great Internet Games Database Api to grab info for our games. Take a look at api.js for the implementation; I won’t go into it here (it’s simple, I promise!). Our Simple route is all we’ll be dealing with; it will follow a common Master-detail view pattern, with the searchable list of games on the left, and a new section on the list for our details.

First, we modify the SimpleForm to hold our view, along with a route to hold our GameDetail component. If you have ever done any other sort of server or client-side routing, this will probably look familiar. If not, don't worry. Let's break it down.

The only major change here is the Route. Like before, it has a path and a render prop that we will use to match the URL we want to use and the component we want to render. We're destructuring the match prop from the other props that React Router gives us and passing it down for use in our detail component. In our case, what we're after is the :name parameter from the route's path. This allows us to have any games referenced using this route within a route (sub-route) so we can provide a nice experience for the user. This name is provided to use by the change we need to make to the links in the GameList component. At the bottom of our GameList, we change the div to a Link component, the same as we did before (don't forget to import it from react-router-dom as we did for the App component).

Note that the Link to prop value matches the route - so we put a game's name at the end of the route, and it matches the Route path we have provided in the SimpleForm component.

All that is left is to create the GameDetail component. You can find the code on Github - it's a little longer so I won't post it all here. Alternatively, you can take a look at the IGDB and implement your own detail page!

Subtle Transitions

For our final act, we’re going to add some slight transitions to make it look a little more magical — and this is the real world, so let’s be honest: we’re going to fudge some loading times with this as well so the perceived experience is better. ;)

First, let’s install a library that will help us get this done.

yarn add react-transition-group

Next, import the dependencies we’ll use:

import { CSSTransition, TransitionGroup } from 'react-transition-group'

Before we change our components, we need to add a css file that we’ll need later.

Add a file named transitions.css to /src/components and copy the following classes into it:

Import this at the top of SimpleForm.js.

import './transitions.css'

Next, we’re going to modify our render method to look like this:

The TransitionGroup is what allows us to list one or more transitions and have them be managed for us. This, in turn, lets us put a CSSTransition component inside and have it manage the mounting and unmounting (and related css styles) transition animations. The key is key, here.

const currentKey = this.props.location.pathname.split('/')[3] || '/'

This tells the CSSTransition when to transition. At this point in our app, if you were to console log the pathname, you might say something like this: /simple/games/Factorio. The game name in this route will change, and that key change tells the CSSTransition to do its thing. The classNames prop is related to the css we added earlier. By convention, this tells the transition to prefix the event names with fade, so we end up with the css classes in our transitions.css file applying the transition animation for fading out as we change routes.

That’s it!

That’s all I have for you, but believe me, there’s plenty more to see (transition prevention, responsive routes, recursive paths, server rendering, etc). The best part is, it’s components all the way down! Make sure and check out the docs for examples of everything mentioned here.

Hats off to Michael Jackson and Ryan Florence for this great library. Follow them on twitter!

I hope this example helped shed some light on the basics of React Router. If you have any feedback, please feel free to leave it in the comments below. Thanks for reading.

Originally published at KeyholeSoftware.

--

--

Mat Warger

Coder, speaker, and life-long student. React, GraphQL, cloud. Sometimes I think too much on the weekends.