Routing

Why?

  • navigating makes the page reload
  • without caching it can be slow
  • avoid page reloads by using a router

How?

Using react-router

  • the following is just an overview, the docs are more detailed

Router

examples use a custom Router based on MemoryRouter

Route

  • docs
  • matches path to components
  • multiple can be active
  • can be nested, side-by-side
  • fully dynamic
  • can have params /some/route/:param

Switch

  • docs
  • ensures that only one Route is active at a time
  • use for fallback (404) routes
  • docs
  • replaces a tags to avoid reloads
  • NavLink can be used to apply “active styles” docs

Redirect

  • docs
  • when rendered, redirect to to prop
  • use it to keep old links working

useParams

  • docs
  • get hold of params in your components

Simple

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

export const Simple: FC = () => {
  return (
    <Switch>
      <Route path="/lorem">lorem</Route>
      <Route path="/ipsum">ipsum</Route>
      <Route path="/dolor">dolor</Route>
      <Route path="/">
        <h1>Home</h1>
        <ul>
          <li>
            <Link to="/lorem">lorem</Link>
          </li>
          <li>
            <Link to="/ipsum">ipsum</Link>
          </li>
          <li>
            <Link to="/dolor">dolor</Link>
          </li>
        </ul>
      </Route>
    </Switch>
  )
}

export default <Simple />

Home

404 Page

import React, { FC } from 'react'
import { NavLink, Route, Switch } from 'react-router-dom'
import classes from './example.module.css'

export const NavBar: FC = () => {
  return (
    <nav>
      <NavLink to="/" exact activeClassName={classes.activeLink}>
        home
      </NavLink>
      {' '}
      <NavLink to="/pricing" activeClassName={classes.activeLink}>
        pricing
      </NavLink>
      {' '}
      <NavLink to="/about" activeClassName={classes.activeLink}>
        about
      </NavLink>
      {' '}
      <NavLink
        to="/this-page-does-not-exist"
        activeClassName={classes.activeLink}
      >
        broken link
      </NavLink>
    </nav>
  )
}

export const Example: FC = () => {
  return (
    <div>
      <NavBar />
      <article>
        <Switch>
          <Route path="/pricing">pricing page</Route>
          <Route path="/about">about page</Route>
          <Route path="/" exact>
            home page
          </Route>
          <Route path="/">404 page</Route>
        </Switch>
      </article>
    </div>
  )
}

export default <Example />

Using Params

import React, { FC } from 'react'
import { Redirect, Route, Switch, useParams } from 'react-router'
import { Link } from 'react-router-dom'
import { Database } from './database'

export const Article: FC = () => {
  const { articleId } = useParams<{ articleId: string }>()
  const article = Database.getArticleById(Number(articleId))
  const user = article && Database.getUserById(article.owner)

  return (
    <div>
      {user && article ? (
        <div>
          <h1>{article.title}</h1>
          <p>{article.content}</p>
          <Link to={`/user/${user.id}`}>
            <i>by {user.name}</i>
          </Link>
        </div>
      ) : (
        <h1>Cannot find article</h1>
      )}
    </div>
  )
}

export const User: FC = () => {
  const { userId } = useParams<{ userId: string }>()
  const user = Database.getUserById(userId)
  const articles = user ? user.articles.map(Database.getArticleById) : []

  return (
    <div>
      {user ? (
        <div>
          <h1>Posts from {user.name}</h1>
          {articles.length ? (
            <ul>
              {articles.map((article) => {
                if (!article) throw new Error()
                const { title, id } = article
                return (
                  <li key={id}>
                    <Link to={`/article/${id}`}>{title}</Link>
                  </li>
                )
              })}
            </ul>
          ) : (
            <p>{user.name} has no articles</p>
          )}
        </div>
      ) : (
        <h1>Cannot find user with id {userId}</h1>
      )}
    </div>
  )
}

export const Users: FC = () => {
  const users = Database.getUsers()
  return (
    <div>
      <h1>Users</h1>
      <ul>
        {users.map(({ id, name }) => (
          <li key={id}>
            <Link to={`/user/${id}`}>{name}</Link>
          </li>
        ))}
      </ul>
    </div>
  )
}

export const Params: FC = () => {
  return (
    <Switch>
      <Route path="/users">
        <Users />
      </Route>
      <Route path="/user/:userId">
        <User />
      </Route>
      <Route path="/article/:articleId">
        <Article />
      </Route>
      <Route path="/">
        <Redirect to="/users" />
      </Route>
    </Switch>
  )
}

export default <Params />