React Router in Flux

Mar 9, 2015

This is a follow up post to the History Store in Flux post where I described how I created my own History store In Flux.

Using React Router for your routing and browser history is a much safer option then doing it yourself hence why I use it now in projects.

React Router is very easy to set up and use but requires a few extra steps to get it working in Flux. Most of this is a combination of code from Dan Abramov (gaearon) and the React Router Flux guide.

The reason you have to do things differently for Flux is explained well in the docs:

Circular Dependencies in Actions

Often times your actions need to call transitionTo on the router, you can’t just require your router instance as a module into your actions because your routes require views which also require the actions, creating a cycle indirectly.

router.js -> routes.js -> SomeComponent.js -> actions.js -> router.js

Therefore, as the docs point out there are 3 options. I choose the option from gaearon:

Proxy calls to router instance and export the proxy early so action creators can require it.

This is better (as will be shown in a minute) as the others didn’t seem as flush. Passing through the router or using a getter to get the instance of the router doesn’t seem as nice as proxying the router and then calling it like normal.

The first thing we need to do is set the router up. Here I’ve seperated the logic up so that we can hold it in different files. This just makes it a bit nice to manage.

Note: some of this is in ES6

router.js
1// routes.js
2var React = require('react');
3var { Route, DefaultRoute } = require('react-router');
4var App = require('./components/App.react');
5
6var routes = (
7 <Route handler={App} path="/">
8 <DefaultRoute handler={App} />
9 </Route>
10);
11
12module.exports = routes;
13
14// router.js
15var routes = require('./routes');
16var Router = require('react-router');
17
18var router = Router.create({ routes: routes });
19
20// app.js
21var React = require('react');
22var router = require('./router');
23
24router.run(function (Handler) {
25 React.render(
26 <Handler />,
27 document.getElementById('app')
28 );
29});

This is great as we can now use the router in our app. However, as explained above we can’t use this in our Flux actions. For this we need to proxy router and export it before anything else (except React itself).

To do this, above our code in router.js we add this:

router.js
1// router.js
2var router;
3
4module.exports = {
5 makePath: function(to, params, query) {
6 return router.makePath(to, params, query);
7 },
8 makeHref: function(to, params, query) {
9 return router.makeHref(to, params, query);
10 },
11 transitionTo: function(to, params, query) {
12 router.transitionTo(to, params, query);
13 },
14 replaceWith: function(to, params, query) {
15 router.replaceWith(to, params, query);
16 },
17 goBack: function() {
18 router.goBack();
19 },
20 run: function(render) {
21 router.run(render);
22 }
23};

This is basically exporting the router functions we need before anything else so that we require this in our actions.

router.js
1// RouterActions.js
2var AppDispatcher = require('../dispatcher/AppDispatcher');
3var router = require('../router');
4var RouterConstants = require('../constants/RouterConstants');
5
6var RouterActions = {
7 transition: function(nextPage) {
8 router.transitionTo('step', {stepUrl: nextPage});
9 AppDispatcher.dispatch({ actionType: RouterConstants.UPDATE_PAGE, nextPage: nextPage });
10 }
11};

This might not be the best place to do the transition, I am still figuring out where the best place for logic like this is; like most other people.

Contact.

LET'S WORK

TOGETHER

I am open to both contract and freelance developer projects and would love to hear what your ideas are.

Feel free to drop me an email and I'll let you know how I can help you!

mail@benstokoe.co.uk