Let the URL do the Talking, Part 2: Bargaining and Acceptance with Redux and React Router

July 19, 2016

In part one of this series, I explored the pains of integrating React Router into purely-functional Redux applications. There, I discovered that not only does React Router dictate an architectural coupling between your state and view layers, but also renders a set of powerful URL-driven Redux patterns impossible.

The root cause of these problems is that we can’t lift URL state from React Router into a Redux store. Without this capability, rich URL data can’t participate in critical application decisions. The URL is a silent observer when it could be a primary source of truth.

We want the URL to do the talking.

The open source community isn’t blind to the problems of using React Router with Redux, and a set of popular libraries provides couple’s therapy for the two systems. As helpful as that sounds, I worried about their utility after deep-diving on the fundamental problems of integrating Redux and React Router. I needed to ask: do these integration libraries address the root issue of lifting URL state into the store?

No, and sort of.

Acceptance

react-router-redux (formerly redux-simple-router) is the more popular of the two integrations, advertising itself as “ruthlessly simple bindings to keep react-router and redux in sync.” It coordinates state sync between the two libraries when using time travel in Redux DevTools. Great, one big integration problem solved! Let’s find out how to read our URL state from the Redux store.

Just kidding! You can’t.

From the README: “You should not read the location state directly from the Redux store. This is because React Router operates asynchronously (to handle things such as dynamically-loaded components) and your component tree may not yet be updated in sync with your Redux state. You should rely on the props passed by React Router, as they are only updated after it has processed all asynchronous code.”

URL state is in the store, but you can’t use it. Confused? You’re not the only one.

In response to a GitHub issue with similar concerns, maintainer Tim Dorr writes: “Don’t use this library to connect Redux middleware and reducers to the Router. There are numerous problems with trying to use that (mostly centered around it being impossible to serialize the router’s state) and not the intended purposes of this library. [Its] sole purpose is to keep track of locations for developer-only features like time travel.”

react-router-redux accepts that lifting URL state from React Router is a tough problem, and therefore doesn’t try (or claim) to solve it. While your DevTools work again, you’re still stuck with container components and a tangled architecture.

Bargaining

redux-router is the old guard of the two integration libraries, and it comes closest to solving the core problem of lifting React Router’s URL state into Redux. Not only does redux-router sync the URL, params, query objects, etc. to the store, but it also guarantees that these values are valid and safe to read, unlike react-router-redux.

Routing stays in sync with Redux dev tools. Selectors that inspect URL state can live above connect(). All is well.

redux-router’s noble goal comes at a price. The library author, Andrew Clark, best explains the limitations of redux-router:

“The [React Router] data is not all serializable (because Components and functions are not directly serializable) and therefore this can cause issues with some devTools extensions and libraries that help in saving the store to the browser session. This can be mitigated if the libraries offer ways to ignore serializing parts of the store but is not always possible.

redux-router takes advantage of the RouterContext to still use much of React Router’s internal logic. However, redux-router must still implement many things that React Router already does on its own and can cause delays in upgrade paths.

“redux-router must provide a slightly different top level API (due to 2) even if the Route logic/matching is identical.”

These limitations cause enough concern to both the maintainers and the community that the README not only brands the library as “experimental”, but also recommends react-router-redux. Since react-router-redux doesn’t solve the same problem, following this recommendation sticks us into a loop.

redux-router is reaching the end of the bargaining stage as it realizes that negotiating a fragile peace with React Router isn’t sustainable.

However, if you’re 100% stuck with React Router, need to read URL state in Redux, and can handle some instability and uncertainty, use redux-router, since it focuses on the important problem of lifting URL state into the store.

Redux-First Routing

After discovering the problems of the two integration libraries, we decided that routing in Redux needed a fresh start. In part 3, we’ll learn about redux-little-router, how it operates from the top (Redux) and not the middle (React), and the useful tools it provides to route flexibly without eating your existing architecture.

Related Posts

The Evolution of urql

December 6, 2022
As Formidable and urql evolve, urql has grown to be a project that is driven more by the urql community, including Phil and Jovi, than by Formidable itself. Because of this, and our commitment to the ethos of OSS, we are using this opportunity to kick off what we’re calling Formidable OSS Partnerships.

Third-party Packages in Sanity Studio V2

November 15, 2022
To get around our "modern language features" issue, we can tweak the Sanity Webpack configuration so that it uses babel to transpile the library files for our third-party libraries that are causing us problems.

What the Hex?

October 24, 2022
If you’re a designer or frontend developer, chances are you’ve happened upon hex color codes (such as `#ff6d91`). Have you ever wondered what the hex you’re looking at when working with hex color codes? In this post we’re going to break down these hex color codes and how they relate to RGB colors.