A proper approach to front-end routing

Routing is a very important part of web development. However, I have a feeling that it’s place in the code architecture is not fully understood, especially when using react-router and similar libraries.

(all code is pseudo)

Say we have a basic page:

<navigation>
<link to=”main” />
<link to=”about” />
</navigation>
<content>
<main>
<user>
<anon>
</main>
  <about>
<user>
<anon>
</about>
</content>

There is a menu at the top, and content underneath, pretty basic.

At this point, let’s split routing into two things:
 1. a browser changes the url, and the page reacts ( e.g. user clicks back button )
 2. the page changes the url ( e.g. user clicks a link on a page )

BROWSER CHANGES THE URL

Starting with the first one, we want the <main> to be shown when url is “/”, and <about> to be shown when the url is “/about”. If the user changes the url, the page should respond. If he clicks the back button, the page should respond, and so on.

So, a developer decides to use react-router here ( or some other library with it’s own methods ). He effectively decided to follow a certain data flow logic.


<Router>
<Route path=”/” component=”main” />
<Route path=”/about” component=”about” />
<Route default whatever… />
<Router>

As we can see, the library dictates how we organize the code.

Just to make things interesting, both <main> and <about> have a <user> component, shown if the user is logged in, or <anon> component if not. The developer now makes a state object, which holds all the data. To decide whether to show <user> or <anon>, he checks the state.user.loggedIn. He also attaches a listener here, so if the user logs in/out, he immediately shows the other component.

<main>

{if user.loggedIn}
<user />
{else}
<anon />
{endif}

</main>
<about>

{if user.loggedIn}
<user />
{else}
<anon />
{endif}

</about>

So, to make it perfectly clear: 
We have two components, <main> and <about>, and we decide which one to show based on a string, the url.
We have two components, <user> and <anon>, and we decide which one to show based on a string, the user.loggedIn (or a boolean, but this hits the sweetspot :D )

Problems: 
 1. two different ways of doing the same thing. Two places, two ways of deciding what to show.
 2. the state object holds the state of the entire app, except that it doesn’t. The url is not in there.

Let’s put the url in the state object, so we have the state.url. We can now attach a listener to this, and decide whether to show <main> or <about> based on this, same as for the other two components.

{if url=”/”}
<main />
{else if url=”/about”}
<about />
{else…}

Ahhh, much cleaner. Now everything follows the same pattern.

Let’s see what we got, shall we? If we get another request, for example, show <user> in <about> if logged in and url===”/about/me”:

<about>
<Router>
<Route path=”/me” component=”someOtherComponent” />
<Route path=”/someone” component=”someOtherOtherComponent” />
<Router>
<about>
<someOtherComponent>
{if user.loggedIn}
<user />
{endif}
</someOtherComponent>

becomes simply:

<about>
{if user.loggedIn && state.url===’about/me’}
<user />
{endif}
</about>

We can attach a listener to the url in the popState event

window.addEventListener(‘popstate’, function (e) {
state.url = newState, or just fire off listeners here, whatever
});

The state object changes whenever the user moves back & forth, and we show the right component.

Now all data flows like this: a change -> update the state object -> fire event -> listeners react

In plain words, what we got is that the URL became just a string, and we can use it to make decisions the same way as for anything else.

Where and how exactly are we going to store the URL is not the concern of this article, although I recommend always having a single source of truth. The browser owns the url, and state object should not store it, but fetch it from the browser every time.

PAGE CHANGES THE URL

What if we need to change the URL? How do we handle clicking on a link? We already have a system which handles the URL changes, so all that’s left to do is change the URL.

Any onClick can call history.pushState method, thus changing the url and leaving the url control to the browser.

The flow: click the button -> tell the browser to change the url -> ??? -> update the state object -> fire event -> listeners react

One more thing to be done…Can you guess what?

The ??? stands for window.dispatchEvent. Don’t forget that pushState doesn’t trigger the above mentioned popState event.

WHAT IS THE POINT OF THIS?

The point is to simplify the logic of the app as much as possible. One way of doing that is having only one way of doing something. This is not a complete guide to app routing, it just shows the general concept to follow.

This is my first article, so please comment on everything and don’t be nice, I would like to know what you think :D