React-router alternative: switch

David Ford
2 min readOct 23, 2016

--

Today, I decided to replace react-router with a JavaScript switch statement.

[Update: 4/13/2020]: Since originally publishing this blog post, I have created many more apps using this technique. And I am still loving it. There are so many 3rd party tools and libraries one must learn to create professional quality web apps today. But in my opinion, a router is not one of them.

Here is what prompted this decision:

  1. I wanted to explicitly pass properties from my top-level component down to lower-level components. I googled around a bit. And passing properties is kind of a pain in react-router.
  2. People on the internet are complaining about the api changes in react-router. There seems to be a v4 coming out soon. And the entire API is going to change.
  3. I realized that, in React, creating basic routing functionality is super simple. So simple, you don’t really need a framework. At least not for my needs. The effort in using the framework is about the same as not using it. This is really my main reason.
  4. If I want to add some fancy stuff later, like prompting the user before leaving a page, making an ajax call before loading a page, or page-level auth, I can do all this with straight JavaScript. No special framework hooks required.

So instead of react-router, I decided to use two other tools:

  1. The JavaScript switch statement
  2. The standard HTML history/popstate APIs (built into modern browsers)

Here is a simplified version of my Main component.

type Props = { user:User, path:string };

export default function Main(props: Props) {
return (
<div>
<h1>Header</h1>
{router(props)}
<h1>Footer</h1>
</div>
);
}

And here is the router function:

function router(props: Props) {
var {key, param} = parsePath(props.path);
switch (key) {
case '':
return <JobMain/>;
case 'jobMain':
return <JobMain/>;
case 'empMain':
return <Employee type="perm" empId={param}/>;
case 'tempMain':
return <Employee type="temp" empId={param}/>;
case 'admin':
return <Admin user={props.user}/>;
default:
return <NotFound path={props.path}/>;
}
}

A few things to note about this:

  1. I am explicitly passing props into my “page” components. Something you can’t do in React Router.
  2. This design is consistent with the React philosophy. The Main view is strictly a function of props.

My paths are fairly limited in this app. All paths will take one of two forms:

  1. With a param: /empMain/1234
  2. No param: /jobMain

And here is my index.js file that makes use of the HTML 5 history/popstate API:

renderApp(window.location.pathname); //render page the first time 

window.addEventListener('popstate', function (e) {
//render page when path changes
renderApp(window.location.pathname);
});

function renderApp(path: string) {
ReactDOM.render(
<Main user={user} path={path}/>,
document.getElementById('root')
);
}

The popstate event fires whenever the user presses back or next in their browser.

And here is my Link component:

export default function Link(props: Props) {

const onClick = (event)=> {
event.preventDefault();
window.history.pushState(null, null, props.to);
window.dispatchEvent(new window.PopStateEvent('popstate'));

};

return <a href={props.to} onClick={onClick}>{props.children}</a>
}

Summary

This simple solution seems to be working quite well, at least for our needs. I would like to get other people’s thoughts.

--

--