Understanding Router5

The main idea behind router5 is to treat routes like any other application data / state. This guide aims to take you through router5's key concepts.

In "traditional" routing, a specific route is associated with a route handler. Such handlers would return your application tree or would link your route to a specific view / component. With router5 it is reversed: rather than the router updating the view, it is up to the view to listen / bind / subscribe to route changes in order to update itself.

The state

router5 is the core repository. Once you have defined your routes and started your router, it only does one thing: it takes navigation instructions and output state updates.

Router

Updating your browser history or listening to URL changes is considered a side-effect, because they are specific to an environment where your application might run (the browser). You can use the browser plugin to update the browser URL and translate popstate events to routing instructions.

A state object will contain:

Tree of routes

Your routes are organised in a tree, made of segments and nodes. At the top will always be an unnamed root node (its name is an empty string ''). It gives you the ability to have nested routes, each node of the tree (except the root node) is a valid route of your application.

For the rest of this article, we will use the following simple example of a few nested routes:

Tree of routes

Transition

During a transition phase, the router will follow a transition path: it will deactivate some segments and activate some new ones. The intersection node between deactivated and activated segments is the transition node. The transition node is very important for your view, as we are about to discover.

Using the tree of routes shown above, let's consider we transition from home to admin.users: the transition node will be the unamed root node, we will deactivate home and activate admin and admin.users.

Transition example #1

Now let's see another example: a transition from admin.roles to admin.users. The transition node will be admin, and the admin segment will remain activated. admin.roles will be deactivated and admin.users will be activated.

Transition example #1

The view

The router is unaware of your view and you need to bind your view to your router's state updates.

This is where you need to forget about route handlers and linking routes to components. On the left you have state updates coming from your router, and on the right you have your application view. Your application view is already in a certain state, and will now have to update to reflect the latest state updates.

On a route change, you only need to re-render a portion of your app. Depending on where you come from, for the same given route, a smaller or larger part of your application view will need to be re-rendered. This is why route handlers are not helpful: routing is not about mapping a route to a component, it is about going from A to B.

Binding to route changes

Your view will need to subscribe to route updates, or specific route updates. There are three types of events you might want to react to, depending on what information you are after:

The last point is the main one. We have seen your routes are organised in a tree. Your components are also organised in a tree, like DOM elements of a page are. In your application, each route node (when active) will have a corresponding component node. Those components should be re-rendered if their associated node is a transition node of a route change.

Below is an example of associated route and component nodes, when admin.users is active:

Transition nodes

The current route is admin.users. If we were to navigate to home, Main would be the component associated to the route node ''. It would re-render to output a Home component instance rather than an Admin one, discarding the whole admin view.

The transition node (as explained above), is the node to re-render your view from.

The listeners plugin makes possible to register those three types of listeners: "any change" listener, route listener and node listener. Note that listenersPlugin has a limit of one node listener per node (listenersPlugin uses router5-transition-path to compute the transition path between two router5 states).

Relation between router and view

In slightly more complicated cases, you might have other parts of your screen to re-render. For example, you might have a header, a main menu or a side panel to update on a route change: in that case you can listen to any route change or a specific route change, and re-render that specific portion of a screen. Keep transition nodes for your "main" content update.

What is router5 best suited for?

Router 5 is best suited for trees of components, where components can easily be composed together. It works best with React, deku, cyclejs, etc...

It also works very well with state containers like Redux: your state container is placed between your view and your router, and your view subscribes to state updates (rather than directly subscribing to route updates). In that case you don't need to use the listeners plugin.

With redux