React Router

While developing with React, a framework for Single Page Applications (SPA), I had a question. React detects changes on a single page and updates the Virtual DOM, rendering only the parts that have changed. So I wondered, "Are all pages the same since there’s no URI distinction?"

However, when you look at popular portal sites, you’ll see that every page has its own unique URI, and these URLs are used to share and differentiate pages. So, does a React-based web application not support routing? The answer is: React does not provide routing out of the box.

In this post, I will explain how to implement routing and assign unique URIs to pages in a React app using the most widely used third-party library, React Router. We will also go through some sample components of the library.

Table of Contents

  • Environment Setup
  • Link
  • Route
  • Redirect
  • Router
  • Switch

Prerequisites

  • Node.js
  • npm (installed with Node.js)
  • Yarn
  • CRA (Create React App)
  • Additional Libraries (React Router)
  • VS Code (Visual Studio Code)
  • Experience with React (see React Learning Part 1)

Installation

  1. Node.js: Download from the official website and install the LTS version suitable for your system.
  2. After Node.js installation, npm is installed automatically. You can use npm commands (see npm documentation).
  3. Install VSCode from Visual Studio Code.

To install Yarn, run:

npm -g install yarn

Install CRA (Create React App) globally via Yarn:

yarn global add create-react-app

Install React Router:

yarn global add react-router-dom

(Use PowerShell or Command Prompt on Windows, Terminal on macOS).

Project Setup

Create a sample React project using CRA. In your Command Prompt or Terminal, navigate to your desired project folder.

Go to project folder(In this case, my documents):

cd C:\Users\Nextree\Documents

On Windows:

npx create-react-app router-master

On macOS:

create-react-app router-master

After creating the project, open it in VSCode by selecting File > Open Folder and choosing the project directory.

Delete all files under the src directory, except for index.js and index.css.

Now, update index.js as follows:

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';

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

Now we’re ready to start creating the sample components!

Before writing the sample components, create a new file SampleRouter.js in the same directory as index.js:

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

class SampleRouter extends React.Component {
    render() {
        return (
            <div>
                <Link to='/link1'>Link1</Link>
                <Link to='/link2'>Link2</Link>
            </div>
        );
    }
}

export default SampleRouter;

Then, update index.js to use SampleRouter:

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import SampleRouter from './SampleRouter';
import { BrowserRouter } from 'react-router-dom';

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

Now, run the server to see the links. In VSCode, open the terminal by selecting Terminal > New Terminal. In the terminal, run:

yarn start

After a moment, the browser will show the two links. Clicking them will take you to http://localhost:3000/link1 and http://localhost:3000/link2.

Route

First, you don’t need to stop the server. The server will automatically reload every time you save a change, so there's no need to manually stop it. If you do want to stop it, press Ctrl + C.

Now, let's go back to SampleRouter.js and update the code as follows:

import React from "react";
import { Router, Route, Link } from "react-router-dom";

class SampleRouter extends React.Component {
    render(){
        return(
            <div>
                <Link to='/link1'>Link1</Link>
                <Link to='/link2'>Link2</Link>

                <Route path='/link1' component={SampleComponent1} />
                <Route path='/link2' component={SampleComponent2} />
            </div>
        );
    }
}

const SampleComponent1 = () => {
    return <div>Hello SampleComponent1!</div>;
};

const SampleComponent2 = () => {
    return <div>Hello SampleComponent2!</div>;
};

export default SampleRouter;

In this code, we are using the Route tag to associate each link with a specific path and component. The first Route has a path='/link1' and component={SampleComponent1}. This means when the /link1 path is accessed, the SampleComponent1 will be rendered.

Save the changes and let's check the sample in the browser. When you click on the links, the corresponding components should be rendered where the <Route> tags are defined.

Redirect

The <Redirect> component works similarly to how redirects are used in general. Let’s consider a scenario where a user requests a certain URL. However, the requested service at that URL needs to redirect the user to another URL. The browser, after receiving the redirect command, will automatically make a request to the new URL.

Here’s how you use the <Redirect> in React Router:

<Redirect to="/news" />

In this case, the user will be redirected to /news. But this can be used in more complex situations. For example, let's say your web application originally provided an API at /news, but later you change it to /good_news. You want users who request /news to be automatically redirected to /good_news. You can do this like this:

<Redirect from="/news" to="/good_news" />

You can also use <Redirect> in conditional rendering scenarios. For instance, let's say you want to redirect a user to the 'My Information' page if they are logged in, and to the 'Login' page if they are not. Here’s how you can do it:

<Route exact path="/my_info" render={() => (
  isLoggedIn ? (
    <MyInformation />
  ) : (
    <Redirect to="/login" />
  )
)} />

This is a route that checks the isLoggedIn condition. If isLoggedIn is true, it renders the <MyInformation /> component. If false, it redirects to /login.

Router

Let’s take a closer look at the BrowserRouter used in index.js. There are six different types of routers in React Router:

  1. Router
  2. BrowserRouter
  3. HashRouter
  4. MemoryRouter
  5. NativeRouter
  6. StaticRouter

Among these, the one we use most often is BrowserRouter. MemoryRouter and NativeRouter are used in non-web environments, while HashRouter is used for older browser support. Router is for low-level customization, and StaticRouter is typically used in server-side rendering environments.

In most cases, we will work with BrowserRouter, as it allows routing within the common web environment. All components inside BrowserRouter can use common properties (like location, history, etc.), so it’s important to place the BrowserRouter at the top of the component tree.

Switch

The <Switch> component wraps around Route and Redirect. While using Switch is not strictly necessary, it can significantly change how routing behaves in your application.

When using <Switch>, only the first matching route or redirect will be rendered. Without it, React Router would render all routes that match the current path. Here's an example to illustrate the difference:

<Link to='/main'>Link1</Link>
<Link to='/sub'>Link2</Link>

<Route path='/' component={Home} />
<Route path='/main' component={Main} />
<Route path='/main/sub' component={Sub} />
<Route component={NoMatch} />

This code is ambiguous. For example, if you visit http://localhost:3000/main/sub, it will match all four paths ('/', '/main', '/main/sub', and the route with no path). Without Switch, all of these components will be rendered.

To resolve this, you can use the exact prop on the Route components, ensuring that a route will only be rendered if the path exactly matches:

<Link to='/main'>Link1</Link>
<Link to='/sub'>Link2</Link>

<Route exact path='/' component={Home} />
<Route exact path='/main' component={Main} />
<Route exact path='/main/sub' component={Sub} />
<Route component={NoMatch} />

Now, /main/sub will only render the Sub component, and /main will only render the Main component. However, there’s still a problem: any route with no path (like Route component={NoMatch}) will match any path and render. To handle this case, we use <Switch>.

<Link to='/main'>Link1</Link>
<Link to='/sub'>Link2</Link>

<Switch>
  <Route path='/' component={Home} />
  <Route path='/main' component={Main} />
  <Route path='/main/sub' component={Sub} />
  <Route component={NoMatch} />
</Switch>

The <Switch> component ensures that only the first matching route is rendered. For instance, if you navigate to /main/sub, it will render Sub, and if you navigate to /main, it will render Main. If none of the paths match, it will render the NoMatch component.

You can also combine the exact prop with <Switch> for even more precise matching:

<Link to='/main'>Link1</Link>
<Link to='/sub'>Link2</Link>

<Switch>
  <Route exact path='/' component={Home} />
  <Route exact path='/main' component={Main} />
  <Route exact path='/main/sub' component={Sub} />
  <Route component={NoMatch} />
</Switch>

Now, only the component for the exact matching path will be rendered, and NoMatch will be rendered if no paths match.

Conclusion

In this post, we explored the basics of using the react-router library, which provides routing functionality in React applications. By understanding the five core concepts—Route, Redirect, Router, Switch, and exact—you can easily set up basic routing in your React app. In future posts, we will dive deeper into each component and explore more advanced routing techniques.

Thank you for reading!

React + MobX
SPA 라이브러리인 React를 MobX state 관리 라이브러리와 함께 배워봅시다.

References