Creating a Custom Router with React and the History API

Overview

Preparation

First, understand the History API. GO TO MDN.

If you're short on time, understanding just pushState and window.popstate should suffice.

Specifications

This router will support the following URLs:

  • /post
  • /post/:id
  • /post/:id/:title

Query parameters are not supported.

Packages Used

We'll skip over the React-related packages.

Other than React, we'll use just one package:

pillarjs/path-to-regexp

This package handles regular expressions for URLs efficiently.

Someday, I want to write my own regular expressions, but for now, I'll rely on this package.

Implementation

Create Components for Navigation and Pages

Prepare components for navigation and the pages corresponding to each navigation link.

Implement Routing

Let's implement the routing.

We'll prepare two components: Router and Route.

Router is a component that switches rendering based on the URL.

Route is a component that simply wraps an anchor (a) tag.

Additionally, we'll create a file called routes.js to define the routing rules.

routes.js will contain an array of objects that map paths to their corresponding components.

By now, you might have guessed the sequence of routing operations:

Initial State (First View)

  1. Retrieve the current URL information.
  2. Render the component that matches the current URL information.

The URL information is stored in the state.

Navigation

  1. Retrieve the path of the clicked link.
  2. Use the History API's pushState to add to the history and navigate.
  3. Re-render the component.

The state is updated, and the component is re-rendered.

The implementation of each component looks like this:

Route.js

Router.js

routes.js

App.js

Note: The strange line breaks in the JSX code might be due to improper ESLint settings.

I referred heavily to You might not need React Router.

The most challenging part of the implementation was figuring out how to retrieve and manage parameter information (e.g., :id). Thanks to the awesome library path-to-regexp, I was able to overcome this issue.

Github

Here is the source code for this implementation:

bmf-san/rubel-router

It is also published on npm:

rubel-router

Thoughts

Using EventEmitter or Observer might make the implementation cleaner... (I need to study more).

References

Articles

Source Code