Section 9 - React Router

We have learnt and coded a lot over the past 8 sections. But it feels like something is still missing, right? We can’t navigate to different pages! What if we want to write a personal website, and let’s say, we want our homepage to be on “/” and our blog posts to be on “/blogs”?

Today we’ll talk about another library that’s commonly used with React - React Router!

Introduction to React Router

  1. First, in the terminal, type in

npm install --save react-router-dom

There are a few versions of React Router. For example, “react-router-dom” is for browsers, while “react-router-native” is for native mobile applications. So what you are learning today will translate perfectly to when you want to develop mobile applications one day!

  1. After the installation, head to “index.js”. There’s a little setup we need to do before we can use React Router in our project! First, import BrowserRouter like so:

import { BrowserRouter } from 'react-router-dom';

  1. Then, in order to give our app the routing functionality that we want, we have to wrap our <App/> component with <BrowserRouter> like so:

<BrowserRouter>

  <App />

</BrowserRouter>

Our index.js should look something like:

import React from 'react';

import ReactDOM from 'react-dom';

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

import App from './App';

import registerServiceWorker from './registerServiceWorker';

ReactDOM.render((

  <BrowserRouter>

    <App />

  </BrowserRouter>

), document.getElementById('root'))

registerServiceWorker();

  1. Now, head back to App.js and clear up everything once again!

import React, { Component } from 'react';

class App extends Component {

  render() {

    return (

    );

  }

}

export default App;

  1. To start things off slowly, let’s just say we want to render a <Blog/> component when you visit “/blog”. To do that, you have to define a route.

  1. First, import Route from the react-router-dom library:

import { Route } from 'react-router-dom';

  1. <Route> is a component that takes in a few properties. The two we need are (1) “path” - the path that triggers the route (i.e. “/blog”) and (2) “component” - the component we want to render when the route is triggered. So we can add the following to our render function:

<Route path='/blog' component={Blog}/>

  1. We don’t have a <Blog/> component yet, so let’s create one! Create a new file Blog.js and put in the basic skeleton as usual:

import React, { Component } from 'react';

class Blog extends Component {

  render() {

    return <h1>Blog</h1>;

  }

}

export default Blog;

  1. Import the <Blog/> component into App.js:

import Blog from './Blog';

And we’re done!

  1. Head to the browser and visit “localhost:3000” (or whichever port your application is being served on). You should see an empty page right now, but if you visit “localhost:3000/blog”, you should see your <Blog/> component being rendered! How cool is that?

The Switch Component

  1. Defining our <Route/> like that is going to cause us problems. To demonstrate why, let’s say we want to another route to show our first blog post at “/blog/1”. Let’s define the new route like so:

render() {

  return (

    <div>

        <Route path='/blog' component={Blog}/>

        <Route path='/blog/1’ component={Post}/>

    </div>

  );

}

  1. Now create a <Post/> component in a new file called Post.js, render any text you want (I’m just rendering “Post”), then import it in App.js. Since we’ve done it so many times, I’ll leave it to you this time!

  1. Head back to the browser. When you visit “/blog”, “Blog” should be rendered as expected. However, when you visit “/blog/1”, you should see both components being rendered!

This is because react-router considers both “/blog” and “/blog/1” to match “/blog” because both of them starts with the “/blog” path. Therefore, when you visit “/blog/1”, both the routes “/blog” and “/blog/1” are matched, and both <Blog/> and <Post/> are rendered. This might be the desired behavior in some cases, but in most cases, we only want ONE component to be rendered for each path. How do we solve this issue?

  1. We will make use of the <Switch> component. First, import Switch into App.js:

import { Route, Switch } from 'react-router-dom';

  1. Then, wrap both our routes in the <Switch> component like so:

<Switch>

  <Route path='/blog' component={Blog}/>

  <Route path='/blog/1’ component={Post}/>

</Switch>

  1. <Switch> will iterate through every <Route> component and will only trigger the first one that match our path.

If you go back to your browser, you’ll find a new problem: you will see <Blog/> when you visit both “/blog” and “/blog/1”! This is because as we said above, “/blog/1” matched with the “/blog” path. Therefore, the first route is triggered, rendering <Blog/>. In other words, the second route will never be reached no matter which path you visit!

  1. There are two ways to solve this problem. The first way is to move the “/blog/1” route before the “/blog” route like so:

<Switch>

  <Route path='/blog/1’ component={Post}/>

  <Route path='/blog' component={Blog}/>

</Switch>

  1. In this way, <Switch> will first check for “/blog/1”:

<Switch>

  <Route path='/blog/1’ component={Post}/>  ←

  <Route path='/blog' component={Blog}/>

</Switch>

So if we visit “/blog/1”, <Switch> will see that it matches the first route, therefore <Post/> will be rendered.

If instead we visit “/blog”, <Switch> will start from the first route:

<Switch>

  <Route path='/blog/1’ component={Post}/>  ←

  <Route path='/blog' component={Blog}/>

</Switch>

And sees that it doesn’t match the first route, and it will move on to the second route:

<Switch>

  <Route path='/blog/1 component={Post}/>

  <Route path='/blog' component={Blog}/>  ←

</Switch>

 And sees that it matches the second route, therefore <Blog/> will be rendered.

  1. The second way to solve this problem is to add an “exact” property to the “/blog” route like so:

<Switch>

  <Route exact path='/blog' component={Blog}/>

  <Route path='/blog/1’ component={Post}/>

</Switch>

Now, our “/blog” route is triggered only when we visit exactly “/blog”, nothing else. I personally think this is a better style, because it is clearer and more readable!

Parameters

  1. Do you notice anything fishy about our code? We manually defined a path for our blog post “/blog/1”. What if we have 50 posts? Don’t we have to define “/blog/1”, “blog/2”, all the way to “/blog/50”? (By the way, this is called a code smell. This is when you sense that your code starts to get repetitive, hard to maintain and well, smelly.)

  1. We could pass in the number as a parameter, just like you would in a function! This time, let’s write a route like so:

<Switch>

  <Route exact path='/blog' component={Blog}/>

  <Route path='/blog/:postid’ component={Post}/>

</Switch>

Notice we appended “:postid” after “/blog/”. Now anything we put after “/blog/” will be stored as “postid”!

  1. Let’s head back to Post.js, and change to render() function to something like:

render(){

  return <h1>{‘Blog  + this.props.match.params.postid}</h1>

}

React Router automatically passes in an object “match” as props to our <Post/> component. The “params” property inside the match object contains all the parameters we passed to the route. Therefore to access the “postid” parameter, we can access this.props.match.params.postid.

  1. Head back to browser, visit “/blog/123” (or any path you like), you should see “Blog 123” rendered!

Just printing the postid on the screen is stupid, I admit. This is just an example. In real applications, you would normally render different things according to the parameter. For example, we could take our post id, fetch the post from the database with the post id, then display it in your website!

Links

  1. We have been typing in URLs into the browser manually. I’m sure you’re tired of that as well. It’s time to add clickable links!

Wait a second, I hear you ask, we could just use an anchor tag right?

<a href=”/blog”>Blog</a>

That works, but it would cause your browser to reload. That’s not very good in terms of user experience. Luckily, React Router offers the <Link> component. Let’s test it out!

  1. In App.js, import Link like so:

import { Route, Switch, Link } from 'react-router-dom';

  1. Here’s what a typical link looks like:

<Link to=”/path”>Click here</Link>

  1. We pass the target path to the “to” property, and we wrap the <Link> component around any text we want. So in our example, we could write something like:

<Link to=”/”>Home</Link>

<Link to=”/blog”>Blog</Link>

  1. And in our Blog.js, we could render something like:

  render() {

    return (

      <div>

        <h1>Blog</h1>

        <ol>

          <li><Link to='/blog/1'>Blog 1</Link></li>

          <li><Link to='/blog/2'>Blog 2</Link></li>

          <li><Link to='/blog/3'>Blog 3</Link></li>

        </ol>

      </div>

    );

  }

Head back to the browser and have fun clicking around!

Summary

Well done! In this section, we first looked into setting up React Router. Then, we learnt to define routes in React Router. They looked something like:

<Route exact path='/pathname’ component={ComponentName}/>

Then, we talked about iterating through routes with <Switch>. After that, we looked into passing parameters to the URL and accessing it from this.props.match.params.*parameter name*. Finally, we learnt to add links to our webpage with <Link>.

Final Code

Here is the code for this section.