Software systems to innovate and grow

Changes In React Router 4

By Engineering Team October 16, 2018 10 mins read

When Michael Jackson and Ryan Florence announced major changes to React Router with v4, the initial response was unsurprisingly mixed. Some liked the new direction and the more React-ish way that RR4 handled routing. Others had a harder time changing the way they reasoned about routing in React. They had a good grasp on the old method and didn’t want to have to rethink everything. And, still others were just irritated at the prospect of a breaking change that would require a ton of refactoring work on their existing projects. Once everyone got their heads around the new methodology, though, it was clear the React Router team had made the right move.

Michael and Ryan had been frustrated with the direction of React Router since v2. The API was limiting, and required a mental model that did not match React’s component-based compositional patterns. Using these patterns, the two spent an afternoon (yes, an afternoon) developing a new routing methodology that fell in line with the rest of React and began working on what would become React Router 4 soon after. It’s best to think of RR4 is as a complete rewrite, though your understanding of previous versions will certainly help. Let’s take a look at some of the changes with this newest router, and see why the community as a whole is on board with it.

Declarative Components: A Quick Primer

So, being a React developer, you know about declarative components, right? Right.

Don’t worry if you only have a vague understanding of this concept. Many developers have trouble nailing down a competent definition of declarative programming, including those in the React community. Declarative programming is an alternative to the more traditional imperative style. A good way to think about these two concepts is to consider the different ways you might go about getting an oil change.

The imperative method would involve taking your car to the shop, but then instructing the mechanic how to change the oil, i.e. the oil change steps, the order of the steps, the type of oil, etc. The declarative way would simply be to take it to the shop and ask for an oil change. You don’t care how they do it, you just declare the end result you want and let them figure out how to produce it.

In React, declarative UI components control flow and state by saying, “I want it to look like this,” instead of, “do it this way, explicitly.” It’s the what instead of the how.

What does this have to do with React Router?

Fair question. With RR4, the team wanted to redesign the router so that it looked and behaved like the rest of React, and that meant converting everything to declarative components. In other words, routes are composable UI components in React Router 4. Routes as components means they can be rendered anywhere in the DOM tree, making them incredibly flexible. It also allows for something else new to React: dynamic routing.

Dynamic vs. Static Routing

In React Router pre-v4 and most JavaScript frameworks, static routing is the only game in town. Routes are listed in what amounts to an external configuration file and included in the app during initialization.

RR4, because of its new component structure, uses dynamic routing that takes place as the app is rendering.

So, what’s the benefit?

I’ll admit, the benefits of dynamic routing are not immediately obvious (or, at least they weren’t to me), but there are several improvements with this new approach:

  • Routes can now be rendered conditionally under specific circumstances, e.g. handling responsive routes in a composable way
  • Nesting routes is now as easy as nesting other React components and no longer requires a nesting API
  • Recursive paths using the aforementioned nested routes can now be used to call on the same component continuously
  • No bulky configuration file outside of the app to manage or reason about

Once you grok the concept of dynamic routing and what it allows you to do, these upgrades will seem like no-brainers. As with everything related to RR4, the key is to think of routes as composable UI components.

Key Components

Since everything is a component in React Router 4, it’s much easier to think about, categorize, and use the API. The three types of components in RR4 are:

  • Router components
  • Router matching components
  • Navigation components

Something else to keep in mind is that react-router has been broken out into two components, one for web and the other for mobile (React Native):

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

The APIs for these differ slightly but are very similar overall. Since this post is focused on web, though, we are going to cover react-router-dom. You can check out the React Native version here.

Routers

To use any of these, you have to install React Router.

npm

yarn

The web router has two options that can be imported from react-router-dom.

  • <BrowserRouter> – used when you have a server that responds to requests (most common)
  • <HashRouter> – used for a static file server

A simple example using the <BrowserRouter> would look like this:

Notice the missing history={browserHistory} in the example. The {browserHistory} object was deprecated for RR4 and is no longer accessible.The withRouter higher-order component is now used to wrap components when exported to capture the match, location, and history props necessary to use <Link> and <Redirect> (more on those in a little bit).

To give you a better idea of how to use withRouter, suppose you needed a Header component that was not a child of a Route, but you wanted to have access to its match prop.

Simply create the component, return its match prop, and wrap it with withRouter:

Matching Routes

Consistent with the minimalist nature of the RR4 API, two components are used for route matching, and one of them (<Route>) is at the center of everything routing-related.

  • <Route> – takes a path and a component
  • <Switch> – contains children <Route>s that can be selected based on the current location

If both are used, they will always be used together, though <Switch> is not technically required. The <Route> component matches the path prop to the current location’s pathname, and the <Switch> component is an option when grouping <Route>s together, particularly if their paths match the same pathname.

Combining the two into a set of conditional routes is pretty simple.

Notice the use of exact in the {Home} route. This takes the place of <IndexRoute> in the previous versions of RR, which was deprecated.

Navigation

We touched on navigation previously when covering routers, but let’s get into more detail.

The new navigation API is, again, pretty simple. There are three main components that can be used to display links and redirect users.

  • <Link> – used to create links in your app
  • <NavLink> – same as <Link>, except with extra styling that keeps it hidden from the user unless its prop matches the current location
  • <Redirect> – used to force navigation in your app

Here’s a quick example of those three:

Like I said, nothing fancy here, which is great. These three cover 98% of your navigation needs and, because of RR4’s composable architecture, can be used anywhere in your app.

Recap

This tutorial only scratches the surface, but the React Router team has a great tutorial that covers things like server rendering, code splitting, and redux integration, so you should definitely check out that out to learn more.

This was a huge change for the community, but the consensus is that it was a needed and well-made change. React Router is now part of React in form and function, and managing routes in your apps will be much easier as a result.

Remember, this was essentially a rewrite of the package, so be patient with yourself, and everything will click into place.

Good luck, have fun, and keep coding!