git.fiddlerwoaroof.com
README.md
3d27451d
 # Routedux — Routes the Redux Way [![npm version](https://badge.fury.io/js/routedux.svg)](https://badge.fury.io/js/routedux) [![Build Status](https://travis-ci.org/cjdev/routedux.svg?branch=master)](https://travis-ci.org/cjdev/routedux)
30db14b1
 
f464bb7a
 <img alt="Route Dux" src="https://upload.wikimedia.org/wikipedia/commons/thumb/d/da/Ducks_crossing_the_road_sign.png/92px-Ducks_crossing_the_road_sign.png" align="right" />
1b712626
 
3d27451d
 Routedux (:duck: :duck: :duck:) routes URLs to Redux actions and vice versa.
f464bb7a
 
c846325a
 (v1.x only works with React >=16)
 
3d27451d
 Your application doesn't need to know it lives in a browser, but your
 users want pretty urls and deep links.
30db14b1
 
ce08d9fd
 ## Wait, my application doesn't need to know it lives in a browser?
30db14b1
 
3d27451d
 URLs are great for finding things on the internet.  But a single page
 application is not the same as a collection of resources that lives on
 a remote server.
1b712626
 
3d27451d
 A single page application is a web application only in the sense that
 it lives on the web.  URLs are are not essential
 to it working well.
ce08d9fd
 
3d27451d
 URLs give users accessing your application in a browser the ability to
 bookmark a particular view in your application so that their
 expectation of browser-based applications will continue to work.
ce08d9fd
 
3d27451d
 We think that's a good thing, but we also don't think the idea of url
 paths should be littered through your application.
ce08d9fd
 
3d27451d
 When you are developing a redux application, you want your UI to be a
 pure function of the current state tree.
30db14b1
 
3d27451d
 By adding routes to that, it makes it harder to test.  And this
 difficulty can be compounded by other decisions about how to add
 routes to your application.
30db14b1
 
ce08d9fd
 ## An alternative approach
30db14b1
 
3d27451d
 React Router is the currently-accepted way to do URL routing in React
 applications.  For a standard React application without Redux, this
 solution isn't too bad.  But once you add Redux, things get difficult.
30db14b1
 
3d27451d
 We basically discovered the same lessons as Formidable Labs:
 [React Router is the wrong way to route in Redux apps.](http://formidable.com/blog/2016/07/11/let-the-url-do-the-talking-part-1-the-pain-of-react-router-in-redux/)
ce08d9fd
 
3d27451d
 However, we don't think their solution
 ([redux-little-router](https://github.com/FormidableLabs/redux-little-router))
 goes far enough, as it still embeds the idea of routes throughout your
 user interface.
ce08d9fd
 
3d27451d
 Once you separate URLs from your application state, you can easily
 port it to other environments that don't know what URLs are, and by
 simply removing the routing declaration, things will work as before.
30db14b1
 
3d27451d
 As an added (and we think absolutely essential) benefit, your entire
 application becomes easier to test, as rendering is a pure function of
 Redux state, and model logic and route actions are entirely
 encapsulated in Redux outside of the app.
30db14b1
 
4676a820
 ## Demo Site
 
 See a simple [demo documentation site.](https://github.com/cjdev/routedux-docs-demo)
 
ce08d9fd
 ## Simple Routing in 25 lines
30db14b1
 
 ```javascript
75dfd458
 import installBrowserRouter from 'routedux';
df6ff92a
 import {createStore, compose} from 'redux';
30db14b1
 
 const LOAD_USER = 'LOAD_USER';
 
 function currentUserId() {
   return 42;
 };
 
 function reduce(state = initialState(), action) {
   ...
 }
 
 const routesConfig = [
   ['/user/:id', LOAD_USER, {}],
   ['/user/me', LOAD_USER, {id: currentUserId()}],
   ['/article/:slug', 'LOAD_ARTICLE', {}],
   ['/', 'LOAD_ARTICLE', {slug: "home-content"}]
 ];
 
df6ff92a
 const {enhancer, init} = installBrowserRouter(routesConfig);
30db14b1
 
 const store = createStore(reduce, compose(
428b0648
   enhancer
30db14b1
 ));
 
1efa47cf
 //when you are ready to handle the initial page load (redux-saga and similar libraries necessitate this being separte)
 init();
 
30db14b1
 ```
 
3d27451d
 Any time a handled action fires the url in the address bar will
 change, and if the url in the address bar changes the corresponding
 action will fire (unless the action was initiated by a url change).
30db14b1
 
 
ce08d9fd
 ## Route matching precedence - which route matches best?
30db14b1
 
3d27451d
 Route precedence is a function of the type of matching done in each
 segment and the order in which the wildcard segments match.  Exact
 matches are always preferred to wildcards moving from left to right.
1b712626
 
 ```javascript
 
     const routesInOrderOfPrecedence = [
3d27451d
 
1b712626
       ['/user/me/update', '/user/me'], // both perfectly specific - will match above any wildcard route
       '/user/me/:view',
       '/user/:id/update', // less specific because 'me' is exact match, while :id is a wildcard
       '/user/:id/:view'
     ];
 
 ```
 
ce08d9fd
 ## Fragment component
30db14b1
 
3d27451d
 Given that every UI state will be in your state tree as a function of
 your reducer logic, you can express any restriction on which parts of
 the UI display, even those that have nothing to do with the specific
 transformations caused by your URL actions.
e7eeaaa7
 
1b712626
 ```javascript
 
 const state = {
   menu: ...
 }
 
 const view = (
   <PageFrame>
       <Fragment state={state} filterOn="menu">
         <Menu />
       </Fragment>
   </PageFrame>
 )
 
 // If menu is truthy, this renders as:
 
 (
   <PageFrame>
     <Menu />
   </PageFrame>
 )
 
 // If menu is falsy, this renders as:
 (
   <PageFrame>
   </PageFrame>
 )
 
 // If property is missing in path, it's falsy.
 
 const view = (
   <PageFrame>
       <Fragment state={state} filterOn="menu.missingProp.something">
         <Menu />
       </Fragment>
   </PageFrame>
 )
 
3d27451d
 // Renders as:
1b712626
 (
   <PageFrame>
   </PageFrame>
 )
 
 ```
3d27451d
 
52e14913
 ## ActionLink and pathForAction(action)
 
 Occasionally it is nice to render URLs inside of your application.
 
3d27451d
 As a convenience, we have attached `pathForAction` to the `store`
 object, which uses the same matcher that the action matcher uses.
 This allows you to create links in your application by using the
 actions.
52e14913
 
 ```javascript
 const routesConfig = [
   ['/user/:id', LOAD_USER, {}],
   ['/user/me', LOAD_USER, {id: currentUserId()}]
 ];
3d27451d
 /* ... do store initialization */
52e14913
 
3d27451d
 store.pathForAction({type:LOAD_USER, id: currentUserId()});
 /* returns /user/me */
52e14913
 
3d27451d
 /* ActionLink */
52e14913
 
3d27451d
 import { ActionLink as _ActionLink } from "routedux";
52e14913
 
3d27451d
 const store = createStore(...);
 
 class ActionLink extends _ActionLink {
     constructor(props) {
         super({ ...props });
         this.store = store;
     }
 }
 
 const action = {
   type: LOAD_USER,
   id: 123
 };
52e14913
 
3d27451d
 return (
   <ActionLink action={action}>Link Text</ActionLink>
 );
 
 /* renders as a link to <a href="/usr/123">Link Text</a> with the text */
 ```
ea9e7709
 
3d27451d
 Now you have links, but your links always stay up to date with your
 routing configuration.