Create predictable ui composition by declaration of the routes tree
react-composite-router
Create predictable ui composition by declaration of the routes tree.
Without Url sharing in any Link or other places. Url must be hidden!
$ npm install react-composite-router --save
import React from 'react';
import { render } from 'react-dom';
import { Router, Slot, Link, routesTree } from 'react-composite-router';
import createBrowserHistory from 'history/createBrowserHistory';
const App = () => (
<div>
<h1>App!</h1>
<Link state="app.settings" params={{ id: 123 }}>Settings</Link>
<Slot name="appBody" />
</div>
)
const SettingsBody = ({ timeStamp, id }) => (
<div>Settings Body! {id}:{timeStamp}</div>
);
// define the routes tree
const tree = routesTree();
const appRoute = tree.createRootRoute('app', {
url: '/?timeStamp',
slots: {
root: App
},
params: {
timeStamp: Date.now() // default value
}
});
appRoute.createChildRoute('app.settings', {
url: '/settings/:id',
slots: {
appBody: SettingsBody
}
});
const history = createBrowserHistory({ basename: '/app' });
render(<Router history={history} routes={routesTree.getRoutes()}><Slot name="root" /></Router>, document.getElementById('root'));
<Router />
Router.propTypes = {
children: PropTypes.node.isRequired,
onChange: PropTypes.func,
routes: routesPropTypes, // routes by name (returns from routesTree.getRoutes())
history: PropTypes.object.isRequired // instance of history.js
};
history - instance of history.js
<Link />
Link.propTypes = {
state: PropTypes.string.isRequired, // state name
reset: PropTypes.bool, // set true, if you want to reset inherited params for transition
params: PropTypes.object.isRequired, // stateParams
reload: PropTypes.bool.isRequired, // if it is true page will be reloaded after click
replace: PropTypes.bool.isRequired, // history.replace
onClick: PropTypes.func,
children: PropTypes.any.isRequired,
disabled: PropTypes.bool.isRequired,
className: PropTypes.string,
activeStateClass: PropTypes.string, // when state or child of this state will be active
activeClass: PropTypes.string, // only if this state will be active
disabledClass: PropTypes.string // only if link is disabled
}
<Redirect />
Redirect.propTypes = { // the same description as Link has
state: PropTypes.string.isRequired,
reset: PropTypes.bool,
params: PropTypes.object,
reload: PropTypes.bool,
replace: PropTypes.bool
}
<Slot />
Slot.propTypes = {
name: PropTypes.string.isRequired, // name of slot
props: PropTypes.object, // props for component
render: PropTypes.func, // render function (Component, props, stateParams, stateName)
children: PropTypes.oneOfType([ PropTypes.func, PropTypes.node ]) // fallback children (if slot will be empty in composition). function (props, stateParams, stateName)
};
routesTree().createRootRoute(name, definition)
routesTree().createRootRoute(name, definition).createChildRoute(childName, definition);
name - String - required
const definition = {
url: '/some/path/:id', // segment of url,
params: {}, // default params
slots: { // slots by name
someSlotName: Component
}
};
usage
import { routesTree } from 'react-composite-router';
const tree = routesTree();
const route = routesTree.createRootRoute('some', { url: '/some' });
route.createChildRoute('some.child', { url: '/child' }); // create an child. name must starts from parent name
HOCs
referring('_statePropName', mapPropsFunc)(Component)
Provide possibility to create your own link or button.
New Component will recognize additional props like:
mapPropsFunc = (props) => ({
state: PropTypes.string.isRequired,
reset: PropTypes.bool,
params: PropTypes.object,
reload: PropTypes.bool,
replace: PropTypes.bool
})
let MyComponent = ({ state, children, ...otherProps }) => (
<div {...otherProps} data-href={state.href} onClick={() => state.apply()}>{children}</div>
);
MyComponent.propTypes = {
state: PropTypes.shape({
href: PropTypes.string.isRequired,
name: PropTypes.string.isRequired, // name of state
apply: PropTypes.func.isRequired, // function for calling if some event was fired
params: PropTypes.object.isRequired, // state params
isActive: PropTypes.bool.isRequired, // true if this state is active (exactly)
isActiveState: PropTypes.bool.isRequired // true if this state or child of this state is active
})
};
MyComponent = referring()(MyComponent);
<MyComponent state="my-state-name" params={{ someParam: 3 }} reload={true} replace={false} reset={true}/>Some</MyComponent>;
Using with redux
this router provide simple redux-reducer. it useful if you are lazy as me =)
import { createStore, combineReducers } from 'redux';
import routerReducer, { changeStateAction } from 'react-composite-router/lib/redux/reducer';
const store = createStore(
combineReducers({
routing: routerReducer
})
);
const onStateChange = (name, params) => {
store.dispatch(changeStateAction(name, params)); // deliberately call store.dispatch or create special handler. up to you
};
render(<Router history={history} onChange={onStateChange} routes={routesTree.getRoutes()}><Slot name="root" /></Router>, document.getElementById('root'));