Learning Basic Routing in ReactJs

Note: This post is part four of Building React Blog SPA – A Long-Term Project series. This post is work-in-progress learning-note and is updated regularly.

React Applications (Apps), unlike multi-page website that most of us are accustomed to, are designed to render multiple components in a single view. When multiple page views are required similar to traditional multi-page web application, those applications are called Single Page application (SPA).

In traditional multi-page web applications, unlike SPAs, navigating a new page requires going to entirely a new page. In SPAs navigating a new page does not involve going to new page but typically loading it inline within the same page (refereed as “views“).

Web Pages vs App Views

Before venturing into React Router, lets learn about difference between traditional multi-page websites that we visit and browse every day and single page application (commonly referred as SPAs).

One of the main features of SPAs is that when a new page is navigated in a browser, the browser is NOT refreshed to load the new page from the page URL but the page (referred as vies) is loaded in line within the main page frame itself.

This creates challenge to serve the page in SPAs as expected by users who expect its behavior similar to tradition multi-page website:

  • The displayed page URL in address bar reflects the visited page (eg. www.mysite.com/contacts).
  • While browsing forward or backward button, expected pages (views) are delivered
  • Any page link (including nested views) can be navigated and delivered as expected. The page URL can be bookmarked, copied and pasted.

Since in SPAs, navigating a new page is not really new page URL, the page view URL must be adjusted appropriately so that browser history is synchronized with the next and previous buttons. Also when a page view is bookmarked it should refer to correct location.

React Router Basics

1. Routers and Selecting a Router

To setup a basic route for different views (pages) in an React application, a React Router component and Route components are essential.

There are two types of routers are available from React Router API and they are <BrowserRouter> and <HashRouter> and create URLs as shown below:

#! <BrowserRouter> URL
https://mysite.com/blog

#! <HashRouter> URL
https://mysite.com/#/blog

The <BrowserRouter> is more popularly used than <HashRouter> and recommended default because <BrowserRouter> tracks router history with HTML5 History API.

2. Router History

The React Training document refers router history as follows:

React history is a JavaScript library that lets you easily manage session history anywhere JavaScript runs. history provides a minimal API that lets you manage the history stack, navigate, confirm navigation, and persist state between sessions.

As stated in React Training/history document & this post, the history object has history.location properties which keeps tracks of current location and its previous locations in a stack. When current location changes, views from new location is re-rendered which provides a sense of navigation.

While navigation, history object uses following methods to change current location:

  • history.push() – invoked when <Link> component is called.
  • history.replace() – is invoked when <Redirect> component is called.
  • history.goBack() – invoked when navigating previous page <Link> to go back to previous entry in history stack.
  • history.goForward() – invoked when navigating forward page <Link> to go to next entry in history stack.
3. The Routes & Links Components

The <Link> component is comparable to HTML anchor element <a> and facilitates navigation between views (pages). In traditional multi-page sites, navigation with the <a> link involves browser refresh and URL change.

// Navigation links
<div className="main-menu"> 
    <ul className="main-nav">
       <li><a href="/">Home</a></li>
       <li><a href="/about">About</a></li>
       <li><a href="/contact">Contact</a></li>
    </ul>
</div>

In contrast, in react Apps the <link> component renders a page component from certain URL as views without a browser refresh.

The way the <Route> in <BrowserRouter> component works is the <Route> component should have a path prop and if location of react App matches with the current App’s location, it gets rendered.

// Navigation with Router links
<div className="main-menu"> 
    <ul className="main-nav">
       <li><<Link to="/">='/'>Home</Link></li>
       <li><<Link to='/about'>About</Link></li>
       <li><Link to='/contact'>Contact</Link></li>
    </ul>
</div>

Additional Information: React Router v4: The Complete Guide | SitePoint

4. <link > vs <NavLink >

The <Link> prop provides declarative, accessible navigation in an application.

//import Link component to App
import { Link } from 'react-router-dom'

//use example in an App
<Link to="/about">About</Link>

In the example above, the <Link> component is imported from react-roter-dom (line 2) and used for navigating About view (line 5).

The <a href="https://github.com/ReactTraining/react-router/blob/master/packages/react-router-dom/docs/api/NavLink.md#navlink" target="_blank" rel="noopener noreferrer"><NavLink></a> prop is similar to <Link> prop but <NavLink> also allows to add styling attributes. In the example below, <NavLink> is imported to an App (line 2) before using later in the App (line 5).

//import NavLink to App
import { NavLink } from 'react-router-dom'

//use example in an App
<NavLink to="/about" activeClassName="current">About</NavLink>

In the example above the activeClassName prop is used to style when About view URL is in active state.

/* css rule for active state */
.current { border-bottom: 2px solid #fff; }

Additional Information: <Link> & <NavLink> | React Training/ React Router

5. Inclusive vs Exclusive Routing

In Router V3, the routing rules were “exclusive”  which allowed only one <Route> that matched to be rendered. Since Router V4, the router rules are “inclusive” meaning every <Route> that matches the location renders at the same time (inclusively).

//a part of an App
<div>
    <Route exact path="/" component={Home}/>
    <Route path="/about" component={About}/>
</div>

In the above code snippets, if location of App is / as in <Home/> or /about as in <About /> (lines: 3-4) it renders both the component are rendered at the same time inclusively because / matches to both. If the exact prop is added, then only matching Home and About components is rendered.

The <Switch> component differs from the <Route> component such that it renders it exclusively. The <Switch> component renders only the first <Route> that matches its location. It is used in exclusive” routing, where only one matching <Route> in a group is rendered.

// a section in an App
<div>
  <Switch>
     <Route exact path="/" component={Home}/>
     <Route path="/about" component={About}/>
     <Redirect to="/" />
  </Switch>
</div>

The use of exact prop is still required or else / would match with /about too and both components are rendered.

The <Redirect> component, when used with <Switch> component only gets rendered when <Route> have no match.

Additional Information: All About React Router 4 | CSS-Tricks

6. Passing Props Down to the Router

While working on react router, it is essential to pass component props down to the React-Router route. Below is the example described in Today I learned:

// define component
<Route
  path="/my/path"
  component={MyComponent}
/>

In the example above, <MyComponent /> in defined (line 4). If we need to pass props down to the <MyComponent /> then render prop with an inline function is used as shown below:

//passing props with render prop
<Route
  path="/my/path"
  render={(routeProps) => (
    <MyComponent {...routeProps} {...props} />
  )}
/>

To quote from the post, “The two spread operator statements are essential. They will pass down the route props that Route would have passed down plus the additional set of props that you want to pass down“.

Additional Information: Router Render Methods | React Training / React-Router & Passing Props Down To React-Router Route | Today I Learned

Assets & Project Overview

This post is part of Building React Blog SPA project and an advanced topic build upon the previous project Creating a Simple Blog Listing React Component, a prior knowledge of the topics listed below is required prerequisite before diving into the routing features discussed in this post.

The Assets

The same components assets used in the previous post Creating a Simple Blog Listing React Component will be refactored and used in this project.

The markup, code snippets and styles to create the Header, Nav, BlogPost and Footer  components are discussed in other posts in the series. Two additional page view components Contact and About will be created in this post.

Goals

In the previous posts in this series, creating components and refactoring them into simple and smaller components is discussed. The main goals of this learning-note post is:

  • To understand basic working of React Router
  • Select appropriate <BrowserRouter> to create URLs in the App
  • Refactor <Nav> and container <App> components to add router component
  • Build and deploy the App in the demo site

Router Installation & Setup

Step 1: Install react-router-dom

The ReactJs routing requires react-roter-dom package. If already not install react-router-dom to App project folder, install the package by issuing following command from the project folder terminal command line:

#! install react-router-dom
npm install --save react-router-dom

#!verify router package is install
react-router-dom --v

#!OUTPUT
6.4.1

Router package installation can also be verified by checking package.json file in src/ folder. If installed, 'react-router-dom' should be listed under dependencies (as shown below).

#! file: package.json
{ ...
  ...
  "dependencies": {
    ...
    "react-dom": "^16.6.1",
    "react-router-dom": "^4.3.1",
    ...
  }
  ...
 }

As noted earlier, assets (structure, page components etc.,) mention in this post below refer to the same assets used in the previous learning post Creating a Simple Blog Listing React Component.

Step 2: Edit src/index.js file

Revisit the src/index.js file from the previous post and make edit as shown below:

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

ReactDOM.render((
<BrowserRouter>
    <App /> 
</BrowserRouter>
), document.getElementById('root'));

In the above example, <BrowserRouter /> component is imported from react-router-dom (line 7). Then the <BroserRouter > component is wrappred around the <App /> (lines: 10-12).

Step 3: Create About.js and Contact.js files

Under components/ folder two component files – About.js and Contact.js are created as views for Contact and About pages (views). The About.js component looks as shown below:

// src/components/About.js 
import React from "react";

const About = () => {
 return (
  <div className="site-main">
      <h1 className="entry-title"> About</h1>
	  <p>Here is brief intro about me. </p>
	  <p>Contrary to popular belief, Lorem Ipsum is not simply 
           random text.It has roots in a piece of classical Latin 
           literature from 45 BC, making it over 2000 years old.</p>
  </div>
 );
}

export default About;

Similar to the About.js component, Contact.js component is created as shown below:

// src/components/Contact.js
import React from "react";

const Contact = () => {
 return (
  <div className="site-main">
      <h1 className="entry-title"> Contact</h1>
	  <p>We can be reached via website <strong>www.mysite.com</strong> 
           or email at <strong>myname@mysite.com</strong></p>
  </div>
 );
}

export default Contact;

Refactoring Folder Structure

1. Edit App Folder structure

The App folder structure described in the previous post was modified to include SiteMain.js component, which is a container component, that works as a frame to hold page views while using router.

#! modified folder structure
react-router-demo
  ...
  src/
    App.css
    App.js
    index.css
    index.js
    image/
       headerimg.jpg
    components/
        Header.js
        SiteMain.js
        Nav.js
        BlogPost.js
        Post.js
        About.js
        Contact.js
        Footer.js
   ...

In the example above, three components SiteMain.js, About.js and Contact.js are added (lines: 13, 17-18). The SiteMain.js component is currently empty and will be created later.

2. Edit Container src/App.js Component

Edit the App.js as shown below to include <SiteMain /> component (line 17).

//src/App.js
import React from 'react';
import './App.css';

import Header from './components/Header';
import Nav from './components/Nav';
import SiteMain from './components/SiteMain';
import Footer from './components/Footer';

const App = () => (
    <div>
      <div>
         <Header />
         <Nav  />
      </div>
      <div>
        <SiteMain />
        <Footer />
      </div>
    </div>
 );

export default App;

In the example above, page layout of the App described in the previous post is divided into two parts <Header /> and <Nav /> components (lines: 13-14), followed by <SiteMain /> & <Footer /> components (lines: 17-18). The SiteMain.js component will replace <BlogPost posts={posts} /> section in line 51 of the App.jsin the previous post.

3. Refactoring src/Nav.js Component

While using React Router, the Nav.js component plays an important role. In this section, the Nav.js component created in the previous post will be revisited and refactored to include <NavLink/> component (line 4) from the react-router-dom as described in the previous section.

// src/Nav.js
import React from "react";

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

const Nav = () => {
  return (
    <div className="main-menu"> 
      <ul className="main-nav">
        <li><NavLink exact activeClassName="current" to='/'>Home</li>
        <li><NavLink activeClassName="current" to='/about'>About</li>
        <li><NavLink activeClassName="current" to='/contact'>Contact</li>
      </ul>
    </div>
  );
 }
export default Nav;

Use of <Link> and <NavLink> in react router has been explain in previous section. The <NavLink> allows to add styling attributes like activeClassName to page views (lines: 10-12).

Highlighting the Active View

The activeClassName attribute defined earlier to style current page view URL in active state was named “current”. A simple CSS rule for current class was defined to highlight current state.

/* css rules for active link*/
.current {
  border-bottom: 1px solid hsl(0, 0%, 60%);
}
Creating Routes

Creating views for pages with <Route> component is described before. As described earlier, it has component and path props and takes exact prop as well. The path is url where component is rendered.

// route section of the app
<div>
   <Route path='/' exact component={Home}/>
   <Route path='/about' component={About}/>
   <Route path='/contact' component={Contact}/>
   {/* when none of the above match, Home will be rendered */}
   <Redirect to='/' /> 
</div>
Exclusive Routing

Creating Inclusive or exclusive routing is discussed before. To render the only first matching <Route /> in path prop exclusively, the <Switch /> the Route component was refactored as shown below:

//refactoring route comp with Switch
<Switch>
   <Route path='/' exact component={Home} />
   <Route path='/about' component={About} />
   <Route path='/contact' component={Contact} />
   <Redirect to='/' />
</Switch>
4. Creating src/SiteMain.js Component

In previous Webpages vs App views section, challenges to view multiple pages in App while using react router was discussed. To summerize briefly, building SPA using React router is slightly different that it contains a ‘container‘ component in the form of a static frame. The individual pages of App are build as separate component. The React Router provides options to select which page component to bring into view in the main container component.

The SiteMain.js component serves as main container frame, which remain static. It will display the content of individual page view represented by Home, About and contact.

Before SiteMain.js componentt is put together, lets discuss and prepare its related section first, which include passing props down to the router & refactoring routing to include pass down prop reference.

Passing Prop Down to the router

How props are passed down to Component in an App is discussed in the previous post. Just to refresh, revisiting <BlogPost /> and <Posts /> components, which are copied below.

//Posts component
import React from "react";

const Post = (props) => {
 return (
  <div className="site-main">
     <header className="entry-header">
      <div className="entry-title">{props.title} </div>	
     </header>{/* .entry-header */}
      <div className="entry-meta">{props.author} </div>
      <div className="entry-content">{props.content} </div>
  </div>
 );
}
export default Post;

The BlogPosts.js components looks as shown below:

//BlogPost Component
import React from "react";

// import the Post component
import Post from "./Post";

const BlogPost = (props) => {
  return (
    <div>
      {props.posts.map(post =>
        <Post 
          key={post.id} 
          title={post.title}
          author={post.author}
          content={post.content}/>
        )
      }
    </div> 
  ); 
} 
export default BlogPost;

In the example above, the <BlogPost /> serves as parent component and <Posts /> component is pass as children with  <BlogPost posts={posts} /> as shown in line 51 of the App.jsin the previous post.

While using react router, the props is passed down to the router using render prop with an inline function as shown in line 4 (below).

//refactoring to pass prop to the component
<Switch>
   {/* rendering props to home page */}
   <Route path='/' exact render={(props) =><BlogPost posts={posts}/>} />
   <Route path='/about' component={About} />
   <Route path='/contact' component={Contact} />
   <Redirect to='/' />
</Switch>
Putting all Together

The SiteMain.js, the container frame of the App was defined earlier is in App struction section. Now that components refactoring for SiteMain.js component frame is complete. Lets tie up all the components together to build the App.

// src/SiteMain.js
import React from "react";
import About from './About';
import BlogPost from './BlogPost';
import Contact from './Contact';
import { Switch, Route, Redirect } from 'react-router-dom';

const posts = [
  {
     id: 1, 
     title: 'Hello World', 
     author: 'Author: Mr Blogger',
     content: 'Welcome to learning React! Lorem Ipsum is not simply random text.'
     },
  {
	  id: 2, 
	  title: 'Learn React', 
	  author: 'Author: Mr reactJs', 
	  content: 'Learning ReactJs. Lorem Ipsum is not simply random text. '
	  },
  {
	  id: 3, 
	  title: 'Learn JavaScript', 
	  author: 'Author: Mr JavaScript', 
	  content: 'Learning JavaScript. Lorem Ipsum is not simply random text.'
	  },
  {
	  id: 4, 
	  title: 'Learn Gutenberg', 
	  author: 'Author: Mr Gutenberg', 
	  content: 'Learning Gutenberg editor. Lorem Ipsum is not simply random text.'
	  },
  {
	  id: 5, 
	  title: 'Learn WordPress', 
	  author: 'Author: Mr WordPress', 
	  content: 'Learning WordPress CMS. Lorem Ipsum is not simply random text.'
	  }
];

const SiteMain = (props) => (
      <Switch>
        <Route path='/' exact render={(props) => <BlogPost posts={posts}/>} />
        <Route path='/about' component={About}></Route>
        <Route path='/contact' component={Contact}></Route>
        <Redirect to='/' />
      </Switch>
    );
export default SiteMain;

In the example above, the standard React is imported from the “react” (line 2), followed by About.js, BlogPost.js and Contact.js components (lines: 3-5). Then router components <Route />, <Switch /> and <Redirect /> were imported from react-router-dom (line 6)

An array of posts with id, title, author and content was defined (lines: 8-39) as an ecample.

Finally, SiteMain component with props was defined as container component (lines: 41-49) with previously defined BlogPost.js component as child component (line 43). Now the routing region created with <Switch /> and <Route /> in the previous section was pasted (lines: 42-47).

Build and Deployment

The App was build and deployed in a website as a sub-folder (eg. https://yoursite.com/your-app) as described in the previous post.

View Demo

Note: To view the demo, click to Home, About & Contact navigation buttons.

Wrapping Up

In the previous learning-note post, a simple post listing component using static data array were discussed. In this post,  basic routing to create pages (About, Contact, etc) components and route them to render different page view in an App’s static container frame are discussed. Creating a nested views, which requires better understanding of React <Route> and Route render methods,   is beyond the scope of this post and covered separately.

Useful Resources and Links

While preparing this post, I have referred the following references extensively. Please to refer original posts for more detailed information.