React Router is a very popular library for handling URL routing in react applications. There has been a lot of controversy recently following the announcement of version 4. For some of our apps at 1stdibs.com, we’ve been following a much different approach to URL routing.
In a Redux application, the store is the single source of truth for the state of your app. A view does two things:
- Render a view of the app state: take the app state from the store and render a visual representation of it to the DOM.
- Handle user events: listen for user input, i.e. click events, etc and dispatch actions to update the state.
This is the basis of the one-way data flow that Redux encourages.
URL Routers are a “view”
For some applications, it makes sense to think of your URL router as another type of “view,” totally separated from the React view layer. Similar to our React views, we use the one-way data flow as the basis of our URL routers, making them predictable and easy to reason about.
This router will have the same concerns as the React layer:
- Render a view of the app state: take the app state from the store and render the current state into the URL using pushState
- Handle user events: listen for user input, this time it will be from the back/forward buttons and new page loads, and dispatch actions to update the state.
Here is an example of what this looks like using ampersand-router. Ampersand-router is basically a standalone version of the router included with Backbone.js. For this example we are building a page where users can see their past orders. It has three main routes:
- /my/orders - a list of all your orders
- /my/orders/1234 - direct deep link to order id 1234
- /my/orders/receipt/1234 - receipt view of order id 1234
First the Redux reducer:
Now the router:
By doing this, your URL logic is a totally separate concern from your view logic. Additionally the Redux store remains the source of truth for application state. This pattern removes application state from the URL in the same way that React and Redux remove application state from the DOM. We follow the same one-way data flow patterns that we follow in React Components.
Links to other routes
In the client-side logic of your app, when a link is clicked, you can intercept the click event, dispatch actions, and render the correct part of your page. If a user right-clicks a link and opens it in a new tab, you need to ensure it renders the correct view. To do this, I usually extract the URL-rendering logic out of the router and into a helper function that can be passed the state. This helper function’s only responsibility is to to render URLs based on the application state. It can be used to render links or write URLs to the location bar.
React is a relatively young ecosystem and there isn’t “one true way” to get things done. Look at your application’s needs and decide if you need a full-fledged routing system with a large API to learn (and possibly rewrite your integration on the next upgrade). We’ve thought a bit differently about URL routing and realized that a small, focused, framework-agnostic library like ampersand-router makes a lot of sense for many different applications.