As the intended audience of this post, you might know that Redux puts all of your application state in a single store, using a beautiful message-based API to change that state as needed.
Before Redux there was the Flux architecture, which did roughly the same, but with many datastores and many attempts at implementation. Things were pretty messy, and Redux brought order and happiness. I ❤️ Redux.
From this I concluded at the time that storing all application state in a single datastore is an important factor in keeping your code maintainable.
Enter the second protagonist of this story, React Router (RR). RR allows you to create a Single Page Application, with URLs that pretend it is a server-rendered Multi Page Application like we used to make in the dark ages. This is very important for using browser history navigation and allowing search engines to link to specific content in your app.
When RR originally came out, back in those wild days of React v0.x, it brought over some ideas about application routing from other frameworks. You would specify Routes using a React-like-but-not-actually-React syntax, wrap your app in a Router and then RR would magically show only the relevant parts of your application.
As a consequence of the “magical” part, a very important piece of the application state was not in the Redux store 🙀. Blasphemy! Sadness! Weeping children! If you wanted to do things depending on the current URL, you either had to use this whole new not-very-declarative RR API, or extract the RR state and feed it into Redux.
In my humble opinion at the time, the current URL was simply a derivation of application state, and not the other way round. I experimented with a system that treated the URL bar like React treats a bound input, so that new URL events were fed into Redux and afterwards the URL was calculated from the store. This worked reasonably well but also did not feel like the best solution.
The main issue with that approach was having to maintain a URL-to-Redux actions mapping as well as a Redux store-to-URL mapping in a single location. This meant that specific application logic needed to go in at least 2 different places in the application, seldom a good sign.
Note, however, that RR had the exact same problem, all the way up to v3.
But now, we get to the part where the glorious future is, in fact, the present: React Router v4 brings a simple, modular API, not “React-like”, but actual React components doing the routing.
Instead of having a single location with all the routes, you can do routing at every level of your application, simply by wrapping blocks in a <Route/>
component which specifies when it should match. You can even do super-advanced routing by using the @withRouter
decorator.
This offers so many advantages: If you want to perform actions when a certain route is selected, you can simply use the React lifecycle callbacks. If you want to extend the API, you can just wrap it. If you want to maintain specific paths in some part of your application, go right ahead.
One specific consequence I really like is that now you can create components that you can use in any location of your application, and they can manipulate the routes for themselves. For example, you could have a list/detail view that automatically adds the id of the current detail item to the URL, and you can even nest it!
So now you might be wondering, what about Redux? What about that single-store ideal?
Well, the application state is still split across two domains, the Redux store and the browser history, but now it is manageable and predictable. You can easily get to location parameters to make your app depend on the URL, and you can very simply combine that with Redux state in your components.
The currently mounted component tree is the result of the declarative route configuration in your application, so the current location and its derived parameters are all you need to know how to render the application, and the Redux store contains all the other application information. These two datastores carry completely orthogonal information, one can change without the other having to update.
While I’m not entirely 100% happy with the API, the modular nature allows wrapping it in a completely interoperable way, so now I can finally say:
I ❤️ React-Router v4.
hugtime, no need to fight!