/react-router-addons-controlled

Controlled Router components for React Router

Primary LanguageJavaScript

react-router-addons-controlled Travis npm package

THIS IS EXPERIMENTAL SO IF YOU USE IT AND FIND PROBLEMS THEY ARE YOUR PROBLEMS THAT YOU MUST SOLVE, MAYBE EVEN ALL BY YOURSELF.

react-router-addons-controlled attempts to treat <Router> like a controlled component (think <input value onChange/>). Unfortunately, it's not that simple. You can't actually control the browser history, so it's more a two-way data binding situation. However, in the majority of cases, it will feel like a controlled component.

Installation

Using npm:

$ npm install --save react-router-addons-controlled

Then with a module bundler like webpack, use as you would anything else:

// using an ES6 transpiler, like babel
import BrowserRouter from 'react-router-addons-controlled/ControlledBrowserRouter'

// not using an ES6 transpiler
var BrowserRouter = require('react-router-addons-controlled/ControlledBrowserRouter')

The UMD build is also available on unpkg:

<script src="https://unpkg.com/react-history/umd/react-router-addons-controlled.min.js"></script>

You can find the library on window.ReactRouterControlled.

Usage

react-router-addons-controlled ships with 2 different Router components that you can use depending on your environment.

There is no <ControlledHashRouter> because hash histories can't provide all of the information we need (I don't think anyway, haven't tried.)

import Router from 'react-router-addons-controlled/ControlledBrowserRouter'
import Router from 'react-router-addons-controlled/ControlledMemoryRouter'

<Router
  history={history}                  // the history object to listen to
                                     
  location={location}                // the location to navigate to

  action={action}                    // the action to use: "PUSH" || "REPLACE", 

  onChange={(location, action) => {  // called when the user clicks
                                     // back/forward buttons
                                     // if you get a "SYNC" action
                                     // YOU MUST ACCEPT IT INTO YOUR STATE
                                     // otherwise it's all busted
  }}

  {...additionalRouterProps}         // all other props supported by the
                                     // uncontrolled "sister" router
/>

So, just like with a controlled input, you respond to onChange by setting state, or dispatching in redux.

What's the point?

The primary motivation for this is to make routing feel like anything else in an app that lifts state into something like redux or mobx. Let's consider Redux:

In Redux application state is kept in the "store". Changes to application state are modeled as "actions". By sticking to these two constraints, the community as able to build things on top like the Redux Devtools. So, to model routing in Redux and othe paradigms like it we need a ways to:

  • Keep the router state (location and action) in the redux store (or mobx observable)
  • Model changes to the location as "dispatched actions" (or mobx... uh, i-dunno-what...)

So intead of rendering a <Redirect> or calling this.context.router.push(location), you can dispatch an action and pass that router state from your store to the Router and it will respond accordingly.

Examples

Please see the examples for more detailed usage:

DID I MENTION THIS IS EXPERIMENTAL?