Skip to content

Dynamic Route Configuration or: How I Learned to Stop Worrying and Love Circular Dependencies

Compare
Choose a tag to compare
@acdlite acdlite released this 16 Sep 04:03
· 170 commits to master since this release

Two big additions with this release.

Routes as children/props

Instead of passing your route configuration to the store enhancer, you now also have the option of passing it to <ReduxRouter> as either the routes prop or as children. This brings us in line with the normal React Router API.

Example:

ReactDOM.render((
  <Provider store={store}>
    <ReduxRouter>
      <Route path="/" component={App}>
        <Route path="parent" component={Parent}>
          <Route path="child/:id" component={Child} />
        </Route>
      </Route>
    </ReduxRouter>
  </Provider>
), el);

or, equivalently:

const routes = (
  <Route path="/" component={App}>
    <Route path="parent" component={Parent}>
      <Route path="child/:id" component={Child} />
    </Route>
  </Route>
);

ReactDOM.render((
  <Provider store={store}>
    <ReduxRouter routes={routes} />
  </Provider>
), el);

This solves the circular dependency problem (#44), where if you needed a reference to the store from within a route transition hook, you had to use a closure. This was confusing and easy to mess up; the new API does this for you using React Router's built-in dynamic routes functionality.

As a bonus, the router will respond to new props, so hot reloading totally works.

Server-side rendering

To render an app on the server, import the reduxReactRouter store enhancer from redux-react-router/server instead of the main module. This is a slightly different version of the enhancer with server-specific functionality. Also import the match() action creator:

import { match, reduxReactRouter } from 'redux-react-router/server'; 

Then set up your store like normal. You can omit createHistory and history if you like:

const store = compose(
  applyMiddleware(m1, m2, m3),
  reduxReactRouter()
)(createStore)(reducer);

The match() action creator is very similar to the match() function provided by React Router, except because it's an action creator, we dispatch the result. It accepts a URL and a callback:

store.dispatch(match('/some/path', (error, redirectLocation, routerState) => {
  if (error) {
    // handle error
  }

  if (redirectLocation) {
    // handle redirect
  }

  if (!routerState) {
    // handle 404
  }

  // Otherwise, render to string
  res.send(
    renderToString(
      <Provider store={store}>
        <Route path="/" component={App}>
          <Route path="some" component={Parent}>
            <Route path="path" component={Child} />
          </Route>
        </Route>
      </Provider>
    )
  );
}));