React Router v5

Learning Goals:

  • Understand and articulate the need for routing
  • Be able to confidently implement React Router in a project
  • Utilize URL params to build dynamic routes
  • Understand how to test components using React Router

Vocab

  • BrowserRouter A <Router> that uses the HTML5 history API (pushState, replaceState and the popstate event) to keep your UI in sync with the URL
  • Router The class that <BrowserRouter> is extended from
  • Route Its most basic responsibility is to render some UI when a location matches the route’s path
  • Link Links provide declarative, accessible navigation around your application
  • NavLink A special version of the <Link> that will add styling attributes to the rendered element when it matches the current URL.
  • Redirect Rendering a <Redirect> will navigate to a new location. The new location will override the current location in the history stack, like server-side redirects (HTTP 3xx) do.
  • Switch Renders the first child <Route> or <Redirect> that matches the location. <Switch> is unique in that it renders a route exclusively (only one route wins).
  • match A match object contains information about how a <Route path> matched the URL.

Prework

Before the lesson, complete the prework

React Router

Review

Prework Review

In small groups, discuss the following questions:

  1. Why use Router?
  2. Describe the high-level process of setting up Router in a project (packages to install, basic component needed)
  3. Describe the following components:
    • Route
    • Redirect
    • Link
    • NavLink
    • Switch

Why Routing?

Routing refers to keeping a webpage up to date with the current url, and vice-versa.

Most of the apps you’ve written so far have been single-page applications. One HTML page whose content is updated through user interactions and JS. These DO NOT use routing.They work fine, but put some limits on the user experience of our applications.

What are some advantages routing can provide?

  • Users can use urls to bookmark pages
  • Users can use the back or forward button
  • Users can easily share content from a page in the app

If you have written a multi-page application, you may have wrestled with Webpack configs in order to get all your pages built successfully.

Fortunately, routing with React is easy! We just need to use a library called React Router.

React Router allows us to conditionally render components based on the current url

The Code

Rather than tell you about how Router works, we’ll work through a series of exercises and examples.

We’ll be using this repo to solve a series of challenges listed below.

Installation Instructions

git clone https://github.com/turingschool-examples/react-router-v5
cd react-router-v5
npm i
npm start

# open your text editor

Code Sand Box Template

You’re also welcome to use the code sand box template, found here.

The App is not fully put together. It has a series of components that will serve as building blocks of the final component. You won’t be building out new components, but you will be editing existing ones.

Setting up Router

Before we break out into groups, we’ll review how to set up Router as a class.

Look through the codebase

Get oriented with the application. Check out all the components, try and write a short summary of what each is doing.

The <Home /> component is rendering a welcome message. Right now, nothing but a nav bar is being rendered by the App. Let’s use router to render the <Home /> component as a landing page.

Remember that React Router conditionally renders components based on the current url. So our goal is to render the component when the user is at the base url.

Setting up Router

To use React Router, we need to wrap any components that will use a React Router-provided-component in some kind of Router component.

We’ll use a Browser Router, since our app will be used in the browser. This Router provides access to the HTML5 History API. But we won’t worry abou those details just yet.

Hint

We’ll come back to this later in the lesson…

The first step is installing react router:

npm install react-router-dom

Once you have React Router installed, import your chosen Router.

// index.js

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App/App';
import { BrowserRouter } from 'react-router-dom';

const router = <BrowserRouter> <App /> </BrowserRouter>;

ReactDOM.render(router, document.getElementById('root'));

Finally, add a Route for the Home component into your App

import React, { Component } from 'react';
import './App.css';
import puppies from '../data/puppy-data.js';
import sharks from '../data/shark-data.js';
import Creatures from '../Creatures/Creatures';
import Home from '../Home/Home';
import CreatureDetails from '../Creatures/CreatureDetails';
import { Route } from 'react-router-dom';


export default class App extends Component {
  render() {
    return (
      <main className="App">
        <nav>
          <a href="/puppies" className="nav">Puppies</a>
          <a href="/sharks" className="nav">Sharks</a>
        </nav>
        <h1>Puppies or Sharks?</h1>
        <Route path="/" component={ Home }/>
      </main>
    );
  }
}

We picked / for the path in the route becuase it designates that there won’t be anything after the URL’s domain name. This represents the base url.


Exercise # 1: Render Puppies

Your goal is click on the word Puppies and see a grid of 9 puppies on the DOM. The page should look something like the picture on the lesson plan. While you may change components as needed, you shouldn’t outright delete content from the page to achieve this.

Take 10 minutes in pairs to get the puppies rendering

Hints:

  • Use the Creatures component. Formatting and styling is handled for you.
  • What additional react-router components should you use? Do any current components need to change?
  • How do you pass props into a component rendered by a <Route /> ?

Solution

/ App.js 

import './App.css';
import puppies from '../data/puppy-data.js';
import sharks from '../data/shark-data.js';
import Creatures from '../Creatures/Creatures';
import Home from '../Home/Home';
import { Route, NavLink } from 'react-router-dom';


export default class App extends Component {
  render() {
    return (
      <main className="App">
        <nav>
          <NavLink to="/puppies" className="nav">Puppies</NavLink>
          <NavLink to="/sharks" className="nav">Sharks</NavLink>
        </nav>
        <h1>Puppies or Sharks?</h1>
        <Route path="/" component={ Home }/>
        <Route path="/puppies" render={() => <Creatures name="puppies" data={puppies} />} />
      </main>
    );
  }
}

Render exact matches

Check out what happens when you take the exact prop out of one of your Routes. Why do you think the Home page is rendering at /puppies?

The exact prop can be used to make sure that partial matches of a URL don't trigger a render.

Render Methods

According to the docs, Routes have three possible methods for rendering a component on match:

  • component
  • render
  • children

We recommend to use render or children – they work more efficiently when re-rendering components. We’ll take a look at some more benefits they provide after the next exercise.

Here’s an example of their syntax: Component

<Route path='/unicorns' component={ Unicorns } />

Render

<Route path='/unicorns' render={ () => <Unicorns /> }

This also allows you to define and pass specific properties to a component dynamically. For example:

<Route path='/ideas/:id' render={({ match }) => {
  const idea = ideas.find(idea => idea.id === parseInt(match.params.id));

  if (!idea) {
    return (<div>This idea does not exist! </div>);  
  }
  return <ListItem match={match} {...idea} />

}} />

Children

<Route path='/other-unicorns' children={ () => <Unicorns /> } />

Exercise # 2: Rendering Sharks

Get the sharks link working as well!

Solution

// App.js
import './App.css';
import puppies from '../data/puppy-data.js';
import sharks from '../data/shark-data.js';
import Creatures from '../Creatures/Creatures';
import Home from '../Home/Home';
import { Route, NavLink } from 'react-router-dom';

export default class App extends Component {
  render() {
    return (
      <main className="App">
        <nav>
          <NavLink to="/puppies" className="nav">Puppies</NavLink>
          <NavLink to="/sharks" className="nav">Sharks</NavLink>
        </nav>
        <h1>Puppies or Sharks?</h1>
        <Route exact path="/" render={ Home }/>
        <Route path="/puppies" render={() => <Creatures name="puppies" data={puppies} />} />
        <Route path="/sharks" render={() => <Creatures name="sharks" data={sharks} />} />
      </main>
    );
  }
}

Route Props

Let’s take a close look at what happens when a Route renders.

Route render methods all provide access to route props, either automatically to the component they render, or via the callback function that the methods take.

These props include:

<Route path='/unicorns' render={ ({ history, location, match }) => <Unicorns /> }

history and location are worth looking into on your own, but today we’ll focus on match.

The match gives us information about how and why the application matched. And it allows us to do some pretty cool stuff.

match.params

The params property of the match prop gives us an object with key value pairs of dynamic url parameters, and any strings that match them.

For instance, we could make our routes for animals more dynamic by doing this:

<Route
  exact
  path="/:animal"
  render={({ match }) => {
    const whichAnimal = match.params.animal === 'sharks' ? sharks : puppies
    return <Creatures name={`I love ${match.params.animal}`} data={whichAnimal} />
  }}
/>

and then navigate to either /puppies or /sharks, we can see that the <Creatures /> component is rendering the correct data based on the params from the url.

params allows us to define shapes of a url that will cause a match, then access the data from that url in our components.

This can be great for dynamically rendering content based on things in the url, like an id. Let’s do that!

Exercise #3: Dynamic Routing


Take a look at the CreatureDetails Component. It takes in all data for a given creature, and displays it on the page.

Your Task is to make a route that will dynamically render a CreatureDetails component for a puppy based on its ID

Hints:

  • Use the CreatureDetails component
  • For example the URL /puppies/1 should render a view just for the puppy with an ID of 1 in the dataset
  • How can you find a one puppy’s data in an array based on its id?

Solution

The new route could look something like this:

// App.js
        <Route
          exact
          path="/puppies/:id"      
          render={({match}) => {
            const { id } = match.params;
            const creatureToRender = puppies.find(creature => creature.id === parseInt(id));   
            return <CreatureDetails {...creatureToRender} />
          }}
        />

Extra Resources:

Tutorials / Guides:

Helpful Articles / Docs:

Check out this additional information on some Router Components:

Provides declarative, accessible navigation around your application.

Things to know:

  • Link can contain an open and closing tag or be a self-closing tag
  • Link takes a to attribute as well as an optional replace attribute
  • to tells the app which path to redirect to. This can be a string or an object
  • replace is a boolean that when true will replace the current entry in the history stack instead of adding a new one
<Link to='/unicorns' />

<Link to='/unicorns'> Unicorns </Link>

A special version of the <Link> that will add styling attributes to the rendered element when it matches the current URL.

It can take the following attributes:

  • activeClassName: string - defaults to active
  • activeStyle: object
  • exact: bool
  • strict: bool
  • isActive: func
  • location: object

Read about each of these here

<NavLink to='/about'>About</NavLink>

Redirect

Rendering a <Redirect> will navigate to a new location. The new location will override the current location in the history stack, like server-side redirects (HTTP 3xx) do. More of a nice to know for now. This is something that can be used if the user does something wrong. ie. went to a route they don’t have permissions to access.

It can take the following attributes:

  • to: string
  • to: object
  • push: bool
  • from: string
<Redirect to='/not/unicorns' />

Switch

Renders the first child <Route> or <Redirect> that matches the location. <Switch> is unique in that it renders a route exclusively (only one route wins). In contrast, every <Route> that matches the location renders inclusively (more than one route can match and render at a time)

<Switch>
  <Route exact path='/' component={Home} />
  <Route path='/users/add' component={UserAddPage} />
  <Route path='/users' component={UsersPage} />
  <Redirect to='/' />
</Switch>

The docs do a great job of quickly showing what Switch is all about.

Lesson Search Results

Showing top 10 results