bbgm-router
TypeScript icon, indicating that this package has built-in type declarations

2.0.2 • Public • Published

bbgm-router

It has been said that writing a router is a rite of passage for a JavaScript programmer. So, here's my router.

Why? Because other routers were pissing me off. In Basketball GM, originally I used Davis.js as the router. But eventually that became unmaintained and some bugs were left unfixed. I switched to Page.js, which was fairly similar. But it too caused me problems, sometimes it would produce mysterious error messages that I never was able to figure out. So I went looking for another router. There's a lot of them. Since I use React, probably I should use Reach Router or React Router, but I don't like coupling my router to my UI library and I don't want to use special Link components instead of normal links. router5 is popular, but its API seems both confusing and a little bit different than my current code, and I didn't want to deal with changing things around. router.js is another option, but the API seems dated.

So I did the only sensible thing and wrote my own. Here it is.

Advantages:

  • No coupling to any UI library
  • Use normal links
  • Nice, clean, modern API (IMHO)
  • TypeScript

Disadvantages:

  • No support for very old browsers
  • Limited options
  • You might not like the API

Surely this is a worse library than any of the libraries I complained above above, so I encourage you to use one of those instead. But it's routing hundreds of thousands of requests over at https://play.basketball-gm.com/ every day, so it can't be that bad!

Installation

$ yarn add bbgm-router

Usage

import router from "bbgm-router";

// Start handling some routes
router.start({
  "/": () => console.log("Index page"),
  "/other": () => console.log("Other page")
});

// Imperative navigation
router.navigate("/other").then(() => console.log("Navigation to other page is complete"));

API

router.start

router.start(
  routes: {
    [key: string]: (Context) => Promise<void>
  }
);

Define your routes and start listening for clicks. The keys of the routes object are paths, and the values are callback functions that will be run when the user navigates to that path.

The Context object passed to the callback function looks like this:

interface Context {
  params: {
    [key: string]: string;
  };
  path: string;
  state: {
    [key: string]: any;
  };
}

params are parameters parsed from the URL. For example, if your route is defined with a path of "/whatever/:foo/:bar" and the user navigates to "/whatever/whats/up", then params will be { "foo": "whats", "bar: "up" }. If your route has no parameters, then params will be an empty object.

path just contains the path of the request like "/whatever/whats/up" in the example above.

state is an empty object by default, unless some other value is supplied to router.navigate.

router.navigate

router.navigate(
  path: string,
  {
    replace = false,
    state = {}
  }: { replace?: boolean; state?: { [key: string]: any } } = {}
): Promise<void>;

Imperatively navigate to a path. The only required parameter is the path, like router.navigate("/my/path.html").

The optional second argument is an object with two optional properties. First, replace. If you set replace to true, then it will replace the latest history entry rather than creating a new one. This is useful when you're doing a redirect and you don't want to add an extra useless entry in the user's history, because that can break the browser's back button.

The other optional property is state. Whatever you put here will appear in the Context of callbacks and events associated with this navigation request. Use this if you need to pass some data along with the navigation request for later use.

router.navigate returns a promise that resolves when the navigation is complete (i.e. when the route callback function is complete and the events are dispatched).

router.addEventListener and router.removeEventListener

Because the Router class is a subclass of EventTarget, these behave just like the standard methods. Events triggered by bbgm-router are CustomEvent objects, with the event.detail property containing both the context of the request (defined earlier) and an error object, if any error occurred:

{
  context: Context,
  error: Error | null,
}

There are two events in bbgm-router:

"routematched" event

This event fires after the user clicks a link and after a matching route is found, but before the route callback function is run. This gives you an opportunity to start any route-related asynchronous activity (such as triggering an analytics tracker, or loading an ad) as early as possible.

router.addEventListener("routematched", (event) => {
  console.log("Route matched for path:", event.detail.context.path);
});

event.detail.error will always be null in this event.

"navigationend" event

This event fires after the navigation is complete. Any error that happens during navigation (such as during the execution of the route callback function) will appear in event.detail.error, giving you the opportunity to show an error page. This event will also fire even if no matching route is found, which you can use to show a 404 page.

router.addEventListener("navigationend", (event) => {
  if (event.detail.error) {
    if (event.detail.error.message === "Matching route not found") {
      console.log("404 error");
    } else {
      console.log("Some other error");
    }
  }
});

Readme

Keywords

none

Package Sidebar

Install

npm i bbgm-router

Weekly Downloads

0

Version

2.0.2

License

Apache-2.0

Unpacked Size

41.7 kB

Total Files

7

Last publish

Collaborators

  • dumbmatter