React.js for the Visual Learner (Chapter 8 | Implementing React Router)

Michael Mangialardi
Coding Artist
Published in
12 min readMay 26, 2017

Prerequisites

Read Chapter 1 | What is This All About?
Read Chapter 2 | What is React and Why is it Cool?
Read Chapter 3 | Building Our Houses
Read Chapter 4 | Our First Village
Read Chapter 5 | Additions to Our Houses
Read Chapter 6 | Building Our First Project
Read Chapter 7 | Building Our First User Interface

Get the Official Ebook

If you want to support me, you can go here to get an official copy of this ebook via PDF, MOBI, and EPUB.

Code Collection

Through Chapter 5: http://bit.ly/2qRNkQX
Chapter 6+: https://github.com/michaelmang/React-for-the-Visual-Learner

The Scope of This Chapter

I previously mentioned that the main focus of this chapter was going to be the creation of a chat bot demo. However, I thought I’d spend a chapter on some preliminary setup before we get to that.

Let me highlight what we will be doing.

In the landing page we just created, there is a link called Demo in the right-hand side of the navigation:

This link currently does nothing. However, we want to toggle off the current view (the landing page) and toggle on a new view (a chat bot demo) in our single page application. We will be going over the solution to this.

In this chapter, we are just going to focus on how to control the active view within a single page application using React Router. We will also go over how multiple views impacts the passing of props.

Fortunately, this chapter should be a bit shorter, and therefore, very refreshing.

Creating Our Demo Village

We will need to make a lot of changes to this but let’s create a basic Demo village.

Create a new file called Demo.js underneath the villages folder:

This file is going to be just like our Landing Page.js except we only want to nest the Header and Footer components like so:

import React from 'react';
import ReactDOM from 'react-dom';
import Header from './neighborhoods/Header.js';
import Footer from './neighborhoods/Footer.js';
class Demo extends React.Component {
render() {
return (
<div className="demo">
<Header/>
<Footer/>
</div>
)
}
}
module.exports = Demo;

Next, we can import and nest this within index.js:

import React from 'react';
import ReactDOM from 'react-dom';
import { Container, Row, Col } from 'react-grid-system';
import LandingPage from './components/villages/Landing Page.js';
import Demo from './components/villages/Demo.js';
class App extends React.Component {
render() {
return (
<LandingPage/>
<Demo/>
)
}
}
ReactDOM.render(
<App/>,
document.getElementById('app')
);

Ok. We now have a LandingPage village and a Demo village. How do we control which one is active?

Implementing React Router

What is it and Why do we need it?

Let’s say we made a single page application that had a landing page and a sign up page. Because it’s a single page application, the landing page and sign up page are both two separate villages (views).

Let’s say we displayed the following landing page:

This is the active village. Now, let’s say we clicked the Sign Up link in the top right corner. We would want that to toggle off the current landing page village and toggle on the sign up village so the following is rendered:

How do we do this?

Well, we discussed conditional rendering back in Chapter 5.

Conditional rendering refers to being able to toggle on and toggle off what components and elements that render.

Here’s an example of applying conditional rendering:

render() {
const isLoggedIn = this.state.isLoggedIn;
return (
<div>
{isLoggedIn ? (
<LogoutButton onClick={this.handleLogoutClick} />
) : (
<LoginButton onClick={this.handleLoginClick} />
)}
</div>
);
}

In the example above, we can toggle a LogoutButton component or a LoginButton component depending if isLoggenIn is true or false.

In Chapter 5, we also discussed how to update data on some type of event. Specifically, we looked at changing data on a user’s click.

Let’s say the single page app without conditional rendering looked like this:

class App extends React.Component {
render() {
return (
<LandingPage/>
<SignUp/>
)
}
}
ReactDOM.render(
<App/>,
document.getElementById('app')
);

In theory, we could control whether the LandingPage or SignUp village component is rendered based on some Boolean flag (data that is either true or false). If someone clicks the Sign Up link, we could change the flag via an event handler so the condition changes and we render the SignUp village in place of the LandingPage village.

There are a couple of issues with this.

  1. It would get really complicated to have a bunch of flags and event handlers to control the conditional rendering for the all of the links in the navigation menu that will change the view.
  2. More importantly, we need to be able to update the URL depending on what view is active.

Continuing off of the second issue, we need to maintain the traditional change of the URL with the change of a page that is adopted for single page applications so that the URL changes on the change of a view.

Fortunately, there is a tool within the greater React ecosystem called React Router.

React Router makes it easy for us to toggle on and off views and updating the URL accordingly.

Meaning, the LandingPage component would display a URL of:

https://airtable.com/

The click of the Sign Up link would toggle off the LandingPage component, toggle on the SignUp component, and update the URL to:

https://airtable.com/signup

All of this can be done neatly using React Router.

Installing via npm

The first step is for us to install React Router via npm.

For that, we can do the following:

npm install --save react-router-dom

Note: -dom is appended to distinguish between -native. The former is React Router for web application and the latter is React Router for mobile (native) applications.

Importing React Router

Now that we have installed React Router, we need to import it to our index.js since it is the highest level of our application where all the villages (views) are directly nested.

Like our react-grid-system import statements, we want to import specific properties.

With React Router, we can add the following import at the top of index.js:

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

We are basically saying: “Hey react-router-dom! I’m going to go ahead and grab BrowserRouter, Route, and Link. By the way, I think BrowserRouter is a bit of a tongue-twister. I’m going to go ahead and refer to BrowserRouter as Router in our code.

Configuring the Router With Routes

Router can be thought of as the encapsulation of all the possible URL paths and the components that are rendered on those paths.

Routes refer to the specific URL path (i.e. /login or /signup) and the view component that should be rendered on that path.

In index.js, let’s add the shell of our Router in place of the two villages we have nested:

class App extends React.Component {
render() {
return (
<Router>

</Router>
)
}
}

Next, we need to add our Routes. Let’s start by adding our LandingPage component as a route on the home :

class App extends React.Component {
render() {
return (
<Router>
<div>
<Route exact path="/" component={LandingPage}/>
</div>
</Router>
)
}
}

The syntax for adding a Route can be seen to actually be quite simple:

<Route path="*insert relative path from home" component={*insert imported component*} />

Note we add exact before path for our home page (path equal to “/”).

Also, note that we have to have an outermost div in between our Router.

Next, let’s add the Route for our Demo village component:

class App extends React.Component {
render() {
return (
<Router>
<div>
<Route exact path="/" component={LandingPage}/>
<Route path="/demo" component={Demo}/>
</div>
</Router>
)
}
}

Now, we will have a URL of

domain/demo //example devbot.io/demo

when our Demo component is made active.

In total, our index.js should look like this:

import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter as Router, Route, Link } from 'react-router-dom'
import { Container, Row, Col } from 'react-grid-system';
import LandingPage from './components/villages/Landing Page.js';
import Demo from './components/villages/Demo.js';
class App extends React.Component {
render() {
return (
<Router>
<div>
<Route exact path="/" component={LandingPage}/>
<Route path="/demo" component={Demo}/>
</div>
</Router>
)
}
}
ReactDOM.render(
<App/>,
document.getElementById('app')
);

Adding Links

cd into the project root, run npm start, and go to the local host address. You will see that our landing page is still rendering:

This means we haven’t broken our code yet. However, our React Router configuration is incomplete.

We need to have the Demo Route activated on the click of the link on the right-hand side of our navigation.

That link is configured in our DemoLink component so let’s open Demo Link.js.

First off, we need to import the Link property from React Router like so:

import { Link } from 'react-router-dom'

The DemoLink component currently looks like this:

class DemoLink extends React.Component {
render() {
return (
<div>
<a href="#">
<span className="demo-link">
Demo
</span>
</a>
</div>
)
}
}

We can replace the <a></a> with Link which we just imported:

class DemoLink extends React.Component {
render() {
return (
<div>
<Link to="/demo">
<span className="demo-link">
Demo
</span>
</Link>
</div>
)
}
}

The syntax here is very straightforward.

Awesome! Let’s test this out. (Note: make sure to update the CSS to match what’s on Github as I made a slight tweak between the writing of Chapter 7 and Chapter 8)

When we click on “Demo” in our navigation, it should now render our Demo component and update the URL:

Now, let’s also add a link back to the landing page (home) on the click of our Branding component.

Updated Branding.js with the following:

import React from 'react';
import ReactDOM from 'react-dom';
import { Link } from 'react-router-dom'
class Branding extends React.Component {
render() {
return (
<div>
<Link to="/">
<img className="branding" src="./images/Branding.svg" />
</Link>
</div>
)
}
}
module.exports = Branding;

We can now go back to the LandingPage view from our Demo view via the logo:

Awesome! We have successfully implemented React Router.

Adding Props

Let’s take a look at what our basic Demo village:

We eventually will make a lot of changes to make this an interactive chat demo. However, we are going to keep the Navigation and CallToAction components.

The issue is that we want to have different text for the Title and Subtitle components within the CallToAction component depending on if we are on the LandingPage view or the Demo view. Create more components is not an option.

How do we resolve this? We can have the text of the Title and Subtitle be controlled by props. We can then pass different props for each view.

We will now see why we practiced passing down props previously in this book.

First things first, let’s inject the text in Title and Subtitle that we will pass later.

Open up Title.js and Subtitle.js.

In each one, let’s replace the text with an inject prop called title and subtitle respectively.

Title.js

import React from 'react';
import ReactDOM from 'react-dom';
class Title extends React.Component {
render() {
return (
<div>
<h1 className="title">{this.props.title}</h1>
</div>
)
}
}
module.exports = Title;

Subtitle.js

import React from 'react';
import ReactDOM from 'react-dom';
class Subtitle extends React.Component {
render() {
return (
<div>
<h3 className="subtitle">{this.props.subtitle}</h3>
</div>
)
}
}
module.exports = Subtitle;

The next step is to pass down these props starting at the Header component in Demo.js and LandingPage.js.

Let’s do the following for Demo.js:

import React from 'react';
import ReactDOM from 'react-dom';
import Header from './neighborhoods/Header.js';
import Footer from './neighborhoods/Footer.js';
class Demo extends React.Component {
render() {
return (
<div className="demo">
<Header title="Chat Bot Demo" subtitle="Watch how it works."/>
<Footer/>
</div>
)
}
}
module.exports = Demo;

…and the following for Landing Page.js:

import React from 'react';
import ReactDOM from 'react-dom';
import Header from './neighborhoods/Header.js';
import SectionA from './neighborhoods/Section A.js';
import SectionB from './neighborhoods/Section B.js';
import SectionC from './neighborhoods/Section C.js';
import Footer from './neighborhoods/Footer.js';
class LandingPage extends React.Component {
render() {
return (
<div className="landing-page">
<Header title="Developer Bot for Slack" subtitle="One article to one random person in your Slack group. Once a day."/>
<SectionA/>
<SectionB/>
<SectionC/>
<Footer/>
</div>
)
}
}
module.exports = LandingPage;

Now, we have defined two different texts for Title and Subtitle for our different views. Next, we need to finish off by working our way down the component hierarchy and passing the props down.

Open Header.js which is the next level down.

This file now can access the props by doing {this.props.___}.

We then want to pass down title and subtitle as props to CallToAction which nests the Title and Subtitle components:

import React from 'react';
import ReactDOM from 'react-dom';
import { Container, Row, Col } from 'react-grid-system';
import Navigation from './blocks/Navigation.js';
import CallToAction from './blocks/Call To Action.js';
import SlackChannel from './blocks/houses/Slack Channel.js';
class Header extends React.Component {
render() {
return (
<section className="header">
<Container>
<Row>
<Navigation/>
</Row>
<Row>
<CallToAction title={this.props.title} subtitle={this.props.subtitle}/>
</Row>
<Row>
<SlackChannel/>
</Row>
</Container>
</section>
)
}
}
module.exports = Header;

Now, let’s open Call To Action.js.

We want to pass the title prop in the Title component and the subtitle prop in the Subtitle component like so:

import React from 'react';
import ReactDOM from 'react-dom';
import { Container, Row, Col } from 'react-grid-system';
import Title from './houses/Title.js';
import Subtitle from './houses/Subtitle.js';
import Button from './houses/Button.js';
class CallToAction extends React.Component {
render() {
return (
<Col lg={12}>
<Title title={this.props.title}/>
<Subtitle subtitle={this.props.subtitle}/>

<Button/>
</Col>
)
}
}
module.exports = CallToAction;

We already injected these in both our Title and Subtitle classes which means we are all set!

Cool beans! We have different text for each view using props.

Before we move on, we can’t forget that we use the CallToAction component (which contains Title and Subtitle) in our SectionC component nested in Landing Page.js.

First, we need to define title and subtitle props in the SectionC component:

import React from 'react';
import ReactDOM from 'react-dom';
import Header from './neighborhoods/Header.js';
import SectionA from './neighborhoods/Section A.js';
import SectionB from './neighborhoods/Section B.js';
import SectionC from './neighborhoods/Section C.js';
import Footer from './neighborhoods/Footer.js';
class LandingPage extends React.Component {
render() {
return (
<div className="landing-page">
<Header title="Developer Bot for Slack" subtitle="One article to one random person in your Slack group. Once a day."/>
<SectionA/>
<SectionB/>
<SectionC title="Developer Bot for Slack" subtitle="One article to one random person in your Slack group. Once a day."/>
<Footer/>
</div>
)
}
}
module.exports = LandingPage;

Then, let’s update Section C.js so that it passes down the title and subtitle props that were defined initially in Landing Page.js:

import React from 'react';
import ReactDOM from 'react-dom';
import { Container, Row, Col } from 'react-grid-system';
import CallToAction from './blocks/Call To Action.js';
class SectionC extends React.Component {
render() {
return (
<section className="section-c">
<Container>
<Row>
<CallToAction title={this.props.title} subtitle={this.props.subtitle}/>
</Row>
</Container>
</section>
)
}
}
module.exports = SectionC;

Sweet! All better.

We now have a taste of how to pass down different props depending on the different villages (views). We also can see the practical use case where we would need to do so.

Final Code

Available here on Github.

Concluding Thoughts

The first time I got React Router working, I remember it was such a cool feeling. I get clicking links back and forth in amazement of the super fast switching between views.

Hopefully, learning React Router was as cool for you as it was for me. In addition, this chapter was a nice change of pace to catch our breath from all the work we’ve been cramming into previous chapters.

Take a breath and get excited because we’ve covered all the major fundamentals for making a React user interface.

The next chapter is going to tie all that we have learned together into a nice bow as we make a chat bot demo.

Chapter 9

Chapter 9 is now available.

Get the Official Ebook

If you want to support me, you can go here to get an official copy of this ebook via PDF, MOBI, and EPUB.

Cheers,
Mike Mangialardi
Founder of Coding Artist

--

--