March 3, 2018

/

React Crypto Stock Compare – Pt. 6: Creating Pages with React Router

React Crypto Stock Compare – Pt. 6: Creating Pages with React Router

This is to part 6 of a multi-part React tutorial that will teach you the basics of React while building a simple app to compare crypto-currency prices to stock prices. You can find the code to work along here and the starting point for this step can be found in the branch using-state-for-api-requests by running git checkout using-state-for-api-requests

The final thing I think is worth getting your head around is how to create new “pages” in a React app. I put pages in quotes because this is a Single Page App (SPA) in the sense that there is only one html file. However using client side routing with React Router you can create the same experience as a regular web page.

React Router

React Router is a separate but well maintained and supported package for managing routing in your app. React Router provides you with a few different components that let you define and control how routing works in your app.

This is easiest expressed by just by showing you so have a look at this updated version of the App.js

import React, { Component } from "react";
import { BrowserRouter as Router, Route, Link } from "react-router-dom";
import "./App.css";

import { PriceTables } from "./price-tables";
import { ComparisonGraph } from "./comparison-graph";

class App extends Component {
  render() {
    return (
      <Router>
        <div className="crypto-stock-compare">
          <header>
            <h1>Crypto Stock Compare</h1>
            <Link to="/">Table Comparison</Link>
            <Link to="/comparison-graph">Graph</Link>
          </header>
          <Route exact path="/" component={PriceTables} />
          <Route path="/comparison-graph" component={ComparisonGraph} />
        </div>
      </Router>
    );
  }
}

export default App;

What I’ve done here is imported the BrowserRouter, Route and Link component from the react-router-dom package. This lets us setup a root router and then within that just I have my route definitions as Route components:

<Route exact path="/" component={PriceTables} />
<Route path="/comparison-graph" component={ComparisonGraph} />

You specify the url that you want to match with the path prop and you specify which component you would like to render if that route is matched with the component prop. The only other thing to explain here is exact which makes sure that the “/” doesn’t match unless its exactly that otherwise other paths would be matched by that since all other routes will contain “/”.

You also might notice in the <header> we have this:

<Link to="/">Table Comparison</Link>
<Link to="/comparison-graph">Graph</Link>

This is how we link to the routes and under the hood when this renders they become anchor tags.

Coding Along

If you haven’t been following along and would like to you can clone the repository and get the starting code for this step by running:

git clone https://github.com/sethreidnz/crypto-stock-compare
git checkout splitting-components

Building out the routes

In order to get the above to work we have to create a few new components and also I’m going to show you a little bit about how I think it’s useful to structure your project while I’m at it.

First create a new folder “src/prices-tables” and new file src/prices-tables/index.js containing:

import React, { Component } from "react";
import { getEthereumDataFromApi, getMicrosoftDataFromApi } from '../api/index';
import { Loader } from "../components/Loader";
import { PriceTable } from "./components/PriceTable";

export class PriceTables extends Component {
  state = {
    ethereumData: null,
    microsoftData: null,
    hasLoaded: false
  }
  async componentDidMount() {
    const ethereumData = await getEthereumDataFromApi();
    const microsoftData = await getMicrosoftDataFromApi();
    this.setState({
      ethereumData,
      microsoftData,
      hasLoaded: true
    })
  }
  render() {
    const { ethereumData, microsoftData, hasLoaded } = this.state;
    debugger;
    if (!hasLoaded) return <Loader />;
    return (
      <div>
        <section className="value-table">
          <h2>Ethereum</h2>
          <PriceTable priceData={ethereumData} />
        </section>
        <section className="value-table">
          <h2>Microsoft</h2>
          <PriceTable priceData={microsoftData} />
        </section>
      </div>
    );
  }
}

This is the comparison tables that we had from App.js moved into it’s own component.

Also while you are at it create a new folder src/price-tables/components and move the components PriceRow and PriceTable into that folder. What I’m getting you to do here is structure your application with Fractal Project Structure.

To explain what this means I’ll contrast two ways of structuring your components and files. You could structure them by their types, for example components, routes, utilities etc. Or you could structure it by their position in the application, for example what route they belong to. The latter is what is the Fractal Project Structure. This means that you create a folder per top level route, and then you place any components that relate to that route inside a components folder within it. Shared components live in the components folder at src/components. I find this really helps to reduce the amount of jumping round you have to do and helps new people coming to the project understand where things are, and where they should go.

Now we have that explained and the files re-organised we need to create our second route. Create a new file src/comparison-graph/index.js:

import React, { Component } from "react";
import { Line } from "react-chartjs";
import { getEthereumDataFromApi, getMicrosoftDataFromApi } from '../api/index';
import { Loader } from "../components/Loader";
import { transformIntoSeriesData } from "../utility";

export class ComparisonGraph extends Component {
  state = {
    ethereumData: null,
    microsoftData: null,
    hasLoaded: false
  }
  async componentDidMount() {
    const ethereumData = await getEthereumDataFromApi();
    const microsoftData = await getMicrosoftDataFromApi();
    const openPriceDataSeries = transformIntoSeriesData(ethereumData, microsoftData);
    this.setState({
      openPriceDataSeries,
      hasLoaded: true
    })
  }
  render() {
    const { hasLoaded, openPriceDataSeries } = this.state;
    if (!hasLoaded) return <Loader />;
    return (
      <div>
        <Line data={openPriceDataSeries} width="600" height="250"/>
      </div>
    );
  }
}

What this component does is use a package “react-chartjs” which uses Chart.js to show a line graph based on the Ethereum and Microsoft data. Normally you would need to install this package but it was already in the package.json from the start so you don’t have to worry about it.

The last thing we need to do is update App.js:

import React, { Component } from "react";
import { BrowserRouter as Router, Route, Link } from "react-router-dom";
import "./App.css";

import { PriceTables } from "./price-tables";
import { ComparisonGraph } from "./comparison-graph";

class App extends Component {
  render() {
    return (
      <Router>
        <div className="crypto-stock-compare">
          <header>
            <h1>Crypto Stock Compare</h1>
            <Link to="/">Table Comparison</Link>
            <Link to="/comparison-graph">Graph</Link>
          </header>
          <Route exact path="/" component={PriceTables} />
          <Route path="/comparison-graph" component={ComparisonGraph} />
        </div>
      </Router>
    );
  }
}

export default App;

Again you would normally have to install “react-router-dom” but I’ve already done that for you in the package.json. Try running the app up in the browser with npm start and have a look at what we’ve made.

You should see initially the original screen that we made in the previous steps except it has a couple of navigation links. If you click on “Graph” you will see the Chart.js graph and the URL will change to ‘/comparison-graph’. This is the basis for how you create different pages in React.

There is a lot more to React Router and far too much to get into in this simple tutorial but if you want to learn more their documentation is great.

If you got lost or want to make sure you did it right you can have a look in the branch in Github or run:

git checkout react-router

Finishing up

To double check that you have everything correct you can just checkout the master branch in the repo. By no means is this a comprehensive tutorial covering all the things you might want to do. But I wanted to keep the tutorial simple and short but cover many of the initial tasks that one need to do.

I’ll be creating a similar tutorial series on some more complex things you need to do such as:

  • Pulling state up to share data between components
  • Using Redux for global state
  • User interaction and creating forms

I hope you enjoyed the tutorial!

Tags:

Seth Reid

/

React Crypto Stock Compare – Pt. 6: Creating Pages with React Router