Routing


Making a SPA feel like a routed app

Innovative Solutions and Technologies Center(ISTC)

Yerevan, Armenia

By Edgar Aroutiounian, Summer 2017

Progress:

SPA


SPA stand for Single-Page-Application. These are Web Applications which don't use separate URLs for different pages. If we step back to the 90s and early 2000's, when you went to foo.com/bar.html from foo.com/home.html then that would give you a new bar.html file from the server. This meant a new HTTP request and a new document, different JavaScript for each HTML page.

As JavaScript became more powerful, programmers starting building entire applications in just one HTML page, continuously changing the HTML elements with JavaScript and the DOM API.

Some downsides of SPA are that JavaScript bundle sizes became large, it takes time to run the HTML, JavaScript is blocking, the user doesn't see anything in the beginning and then everything at once and we lose routes.

Routes


Losing routes was a big downside to SPA based applications because routes convey semantic information. For example, everyone knows that foo.com/login means a login page, or foo.com/friends-list means we expect to see some kind of social media. A SPA that only shows foo.com and continuously mutates the DOM without letting the user know what route we're on loses a critical chance to have a good experience with the application.

The browser gives us a way to manipulate the browser history/URL with the history API.

https://developer.mozilla.org/en/docs/Web/API/History

React-Router


React applications are all SPAs since React is changing the contents of the DOM for us whenever render is called. We use a library called React-Router to manage the history API for us whenever different components render. It is not strictly necessary to use React-Router but it will make the experience much better for our users. With React-Router we get to keep our React SPA model and still have beautiful routes.

$ yarn add react-router react-router-dom

Notice that this is two separate packages, just like React, the core react-router library does not assume the existence of the DOM.

react-router uses some deeper concepts, like Higher Order Components (HOC), this basically means we give Components to other Components and those Components decide what to render 😲

https://reacttraining.com/react-router/web/example/basic

Getting started (Offical example)


NOTE: There should be no space between $ and {} in the match usages, this is because of how the lecture notes are made using ES6 template strings

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

const BasicExample = () => (
  <Router>
    <div>
      <ul>
        <li><Link to="/">Home</Link></li>
        <li><Link to="/about">About</Link></li>
        <li><Link to="/topics">Topics</Link></li>
      </ul>
      <hr/>
      <Route exact path="/" component={Home}/>
      <Route path="/about" component={About}/>
      <Route path="/topics" component={Topics}/>
    </div>
  </Router>
)
const Home = () => <div><h2>Home</h2></div>
const About = () => <div><h2>About</h2></div>
const Topics = ({ match }) => (
  <div>
    <h2>Topics</h2>
    <ul>
      <li><Link to={`$ {match.url}/rendering`}>Rendering with React</Link></li>
      <li><Link to={`$ {match.url}/components`}>Components</Link></li>
      <li><Link to={`$ {match.url}/props-v-state`}>Props v. State</Link></li>
    </ul>
    <Route path={`$ {match.url}/:topicId`} component={Topic}/>
    <Route exact path={match.url} render={() => (
      <h3>Please select a topic.</h3>
    )}/>
  </div>
)
const Topic = ({ match }) => <div><h3>{match.params.topicId}</h3></div>

Notice how we use Route, Link and Router. We will use these Components very often, they are the building blocks of react-router.

Assignment


Now let's do something practical. Let's make a UI to display currency conversion rates. Use this API to get the currency rate:

http://api.fixer.io/latest?base=USD

Using react-router and giving a dedicated route, show the prices for JPY, EUR, GBP and USD. You can do this by replacing the query parameter here: base=CURRENCY_NAME_HERE .

HINT: Make your Root component hold the state, when will it get it?, How will you pass data around?