Skip to content

Instantly share code, notes, and snippets.

@danbahrami
Last active September 2, 2019 13:32
Show Gist options
  • Save danbahrami/6f992708c72d685b7f64840b9783b8ef to your computer and use it in GitHub Desktop.
Save danbahrami/6f992708c72d685b7f64840b9783b8ef to your computer and use it in GitHub Desktop.
What to expect with React Router V5

Welcome to React Router V5

Until now we've been on React Router V1. While some parts of the API look similar between the two versions they are very different in a lot of ways.

react-router and react-router-dom

First of all, there are now two packages for React Router. In a similar way to how react-dom was split from react, react-router is now the core (cross-platform) library with react-router-dom being the DOM specific implementation. react-router contains all the general routing components:

  • <Router />
  • <Route />
  • <Redirect />

While react-router-dom contains all the components that render actual DOM elements

  • <Link />
  • <NavLink />

Nested <Route /> components

The big difference with the V5 is that <Route /> components can now be rendered anywhere in the JSX tree. They previously had to sit directly under a Router. So before we had one file where all our routes were defined:

<Router>
  <Route path="/foo" component={Foo} />
  <Route path="/bar" component={Bar} />
  <Route path="/foo/bar" component={FooBar} />
</Router>

We can now be less centralized and nest routes in our own components. Let's take a look at the admin section as an example. Our old routes looked like this:

// routes.js
<Route path="/admin(/)" name="admin" component={Admin}>
  <Route path="people(/)" component={PeoplePage} />
  <Route path="groups/:groupId(/)" component={SingleGroupPage}>
    <Route path="dashboards(/)" component={DashboardsGroupPage} />
    <Route path="data-sources(/)" component={DataSourcesGroupPage} />
    <Route path="loops(/)" component={LoopsGroupPage} />
  </Route>
</Route>

Now, all the routing could happen from the <Admin /> component

// routes.js
<Route path="/admin" component={Admin} />

// admin-component.js
<div>
  <SharedAdminHeader />
  <Route path="/admin/people" component={PeoplePage} />
  <Route path="/admin/groups/:groupId/dashboards" component={DashboardsGroupPage}/>
  <Route path="/admin/groups/:groupId/data-sources" component={DataSourcesGroupPage}/>
  <Route path="/admin/groups/:groupId/loops" component={LoopsGroupPage}/>
</div>

Render props

There are now 3 ways to render a component from a <Route />.

component prop

This is the same as Router V1, you pass a component constructor and when the path of the route matches the component will be rendered and passed 3 props

  • location an object describing the current location
  • history the history object
  • match an object that contains all the matching url parameter values
<Route path="/admin/people" component={PeoplePage} />

render prop

This is useful if you want to take values from the URL and pass them straight to the component without the component having to care about the URL. When the path of the route matches it will call the render function with the same 3 props as above and will render the returned value.

<Route 
  path="/admin/groups/:groupId/dashboards" 
  render={({ match, location, history}) => (
    <DashboardGroupsPage groupId={match.params.groupId} />
  )}
/>

Child function render

You can pass a function as a child, this is almost exactly the same as the render prop except that this function will be called even when the path isn't matching. If the path isn't matching the match value passed to the function will be null. This can be useful for rendering modals for example, where you always want to render the component but want to switch the isVisible prop on when the path matches. This was previously very fiddly to do. Now it's easy!

<Route 
  path="/admin/groups/:groupId/users/invite" 
 >
  {({ match, location, history}) => (
    <UserInviteModal isVisible={!!match} groupId={match && match.params.groupId} />
  )}
</Route>

The <Switch /> component

By default, any route that matches the path will be rendered, that means a number of matching routes could be rendered at the same time. In cases, like the admin page, where you only want to render a single child you can use the <Switch /> component, and it will only ever render the first matching route.

Here's an interesting case:

<Route path="/edit/dashboards/new" component={NewDashboard} />
<Route path="/edit/dashboards/:dashboardId" component={EditDashboard} />

In Router V5 the url /edit/dashboards/new would match both routes, in the second case the value of match.params.dashboardId would be the string 'new'. And both the NewDashboard and EditDashboard components would be rendered. Obviously we only want to render the NewDashboard component so we would wrap them in a <Switch />.

<Switch>
  <Route path="/edit/dashboards/new" component={NewDashboard} />
  <Route path="/edit/dashboards/:dashboardId" component={EditDashboard} />
 </Switch>

Now when the URL is /edit/dashboards/new it matches the first route so the switch component stops looking any further and renders the NewDashboard. When the URL is /edit/dashboards/123 it doesn't match the first route but does match the second and so the EditDashboard component is rendered.

That's all the important parts. Also remember. Rendering an <a> tag will completely avoid any client side routing and will hard refresh the page. When navigating inside the app always try to use <Link />

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment