July 02, 2019

A Programmer’s Guide to React Router

by Camilo Reyes

A Programmer’s Guide to React Router

Modern apps are data-driven to where routes can change dynamically based on back-end data.

React Router is a lightweight library that does dynamic routing. It works like any React component because URL routes can mutate when rendering components. In this take, we’ll explore React Router using stock data. We’ll put the data in a static JSON file, but this data can come from dynamic sources.

You can find the complete source code for this tutorial in this GitHub repo.

Feel free to follow along, we’ll assume no knowledge of React Router and little experience with React. To begin, fire up a new create-react-app:

npx create-react-app programmers-guide-react-router --typescript

We’ll include TypeScript with a --typescript parameter to add type safety. This helps to conceptualize stock data that goes in each route.

Go ahead and keep the existing App.tsx as we’ll be reusing some of it. The initial Create React App template has enough to begin minus one dependency. To install React Router, do:

npm i react-router-dom --save

Because it needs type definitions, do:

npm i @types/react-router-dom --save-dev

Inside App.tsx, import all components necessary:

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

We’ll cover what each one does as we flesh out stock data with data-driven routes. Be sure to gut the App component down to the bare bones:

class App extends Component<{}, AppState> {
 render() {
   return (
     <div className="App">
       <header className="App-header">

         // TODO: dynamic routes go here

         <p style={{position: "absolute", bottom: 0}}>
           Programmer’s Guide to React Router
         </p>
       </header>
     </div>
   );
 
}

The data will be a list of stock information, for example, stock symbol, name, and description. To help visualize this in TypeScript, add a state type declaration:

interface AppState {
 stocks: Array<StockState>
}

interface StockState {
 name: string;
 symbol: string;
 description: string;
 value: number;
}

These types can define state in the App component:

state: AppState = {
 stocks: []
};

Navigation data that generates the link collection comes from a JSON file on the server. We’ll use fetch to put this stock data in place and mutate state:

async componentDidMount() {
 const payload = await fetch('/stocks.json');
 const data: Array<StockState> = await payload.json();

 this.setState({stocks: data});
}

We’re using async/await by placing an async prefix in componentDidMount. This makes the code block look like synchronous code to improve readability. The await keyword expects a Promise<any> and only executes the next line when fulfilled.

Inside the render() method, a map function is useful for getting a list. The NavLink component creates the link collection:

const links = this.state.stocks.map(s =>
 <NavLink to={"/" + s.symbol}
   key={s.symbol}
   className="App-link" style={{marginRight: "1em"}}
   activeStyle={{color: "white", textDecorationLine: "none"}}>
   {s.symbol}
 </NavLink>);

Note the key prop — React needs this anytime it has a list of components. The to prop has the route where the link will go to. Be sure to prefix this with "/" so it matches the current route and activates. The activeStyle prop sets the CSS style when the route is active.

Place the link collection inside the return() statement, right below the header tag:

<BrowserRouter>
 <p>
   {links}
 </p>
</BrowserRouter>

The NavLink component must be wrapped around a BrowserRouter component. This enables dynamic routes in React Router.

Protect your React App with Jscrambler

Dynamic Routers

The Route component renders any React component when the route is active. Using stock data, we can create a list of components per route:

const routes = this.state.stocks.map(s =>
 <Route key={s.symbol} path={"/" + s.symbol}
   render={() => <StockComponent {...s} />} />);

Note the prefix "/" — this is so it has an exact match to the route. With the render prop, it is possible to set any number of props in the rendering component. The StockComponent will use props that come from stock data when it renders. React Router uses rendering props to give you this flexibility. The render prop takes a callback function with a single parameter. For example, (props) => <></>, placing a props argument in the callback gives you access to the parent’s props.

These routes can go below the link collection in the BrowserRouter component, for example:

<Switch>
 {routes}
</Switch>

A Switch component works much like a switch statement in JavaScript. It picks the first match, renders the route component, and exits. If you have a Route that’s set to match a homepage like "/", be careful. It’ll need an exact prop in the Route component. Or set it as the fallback route by placing it last in the switch component.

Stock Component

For the StockComponent, props come from StockState. We can inherit StockProps from state to make this distinction clear in code. For example:

interface StockProps extends StockState {
}

This is a nice clean way of reusing type declarations while making the code explicitly clear. The stock component can then use StockProps as the type:

const StockComponent: React.FC<StockProps> =
 ({name, symbol, value, description}) =>
 <>
   <p style={{textAlign: "left"}}>
     {name} <br />
     Symbol: {symbol} <br />
     Value: ${value}
   </p>

   <p style={{textAlign: "justify", margin: "1em 2em"}}>
     {description}
   </p>
 </>;

This is a functional component, or often called a stateless component, because it does not mutate state. TypeScript makes it easy to conceptualize this component while adding type safety.

Clicking on each link now renders the stock component with route prop data. Because it is dynamic, changing the JSON file also changes the link collection and each route.

This is what all the components look like:

React Router example

Conclusion

React Router is a nice simple way to put in place dynamic routing. The BrowserRoute component encapsulates both navigation links and route components. The NavLink component has props like activeStyle to change style when the route activates. Dynamic routers can go in a Switch component that acts like a switch statement. For each route, a React component can render with rendering props.

The library has a lot of flexibility so you can render all React components in a single page. It has features that work well in the browser, such as navigating between routes with the back button. All this, while embracing React components in a lightweight package.

Lastly, don't forget that if you're building React applications with sensitive logic, you should always protect them against abuse, tampering and reverse-engineering by following our guide.