@mamawosh/router-config
v0.1.0-beta.4
Published
Mama Wosh router config UI component
Downloads
5
Maintainers
Readme
mamawosh-router-config
Mama Wosh Router Config
A set of convenience features on top of react-router-config
, a component tree hierarchy generator for react-router
.
System Requirements
Install the latest versions of the following software (some have been given minimum viable versions):
- Node.js v8.2.0, which is prepackaged with
- NPM v5.2.0, which is prepackaged with
- NPX v5.2.0
- NPM v5.2.0, which is prepackaged with
- Yarn (the
/example
folder uses this instead of NPM, highly recommended) - Visual Studio Code (highly recommended) or Sublime Text
version-bump-prompt
will help automatically adjust the semantic version of the library before you publish any new changes
Installing version-bump-prompt
Have NPM install it globally.
npm install --global version-bump-prompt
Usage
Prerequisites
Before installing this package, make sure that the latest versions of these packages have already added to your dependencies:
react
react-router
react-router-config
- All
@types/*
definitions for the packages mentioned if your project usestypescript
.
Commands
Install this package:
NPM
npm install --save @mamawosh/router-config
Yarn
yarn add @mamawosh/router-config
Example
Let's assume that your project directory looks like this:
awesome-project
├── node_modules
├── public
├── src
│ ├── components
│ │ ├── Hello.jsx
│ │ ├── User.jsx
│ │ └── World.jsx
│ ├── pages
│ │ └── NotFound.jsx
│ ├── root.reducers.js
│ ├── root.routes.js
│ ├── Root.jsx
│ └── index.js
... other files
Create a file containing all of your routes. Call this your "root route" file.
awesome-project/src/root.routes.js
import { RouteConfigComponent, patchRouteConfigPaths } from '@mamawosh/router-config';
import NotFound from './pages/NotFound';
import Hello from './components/Hello';
import World from './components/World';
import User from './components/User';
const rootRoutes = patchRouteConfigPaths([ // Prefixes your route trees so that you don't have to!
{
path: '/',
component: RouteConfigComponent, // Renders the subroutes without adding any extra content.
routes: [
{
path: '/hello',
component: Hello,
routes: [
{
path: '/'
exact: true,
component: RouteConfigComponent,
},
{
path: '/world',
component: World,
},
{
path: '/:user',
component: User,
},
],
},
]
},
{
path: '*',
component: NotFound,
},
]);
// Logging rootRoutes results in:
// [
// {
// path: '/',
// component: f RouteConfigComponent(),
// routes: [
// {
// path: '/hello',
// component: Hello,
// routes: [
// {
// path: '/hello'
// exact: true,
// component: f RouteConfigComponent(),
// },
// {
// path: '/hello/world',
// component: f World(),
// },
// {
// path: '/hello/:user',
// component: f User(),
// },
// ],
// },
// ]
// },
// {
// path: '*',
// component: f NotFound(),
// },
// ]
export default rootRoutes;
Render the entire route tree at your root component:
awesome-project/src/Root.jsx
import React from 'react';
import { BrowserRouter } from 'react-router-dom';
import { renderRoutes } from 'react-router-config';
import rootRoutes from './root.routes';
function Root() {
return (
<BrowserRouter>
{renderRoutes(rootRoutes)}
</BrowserRouter>
);
}
export default Root;
Apply the same renderRoutes
to each file expecting subroutes:
awesome-project/src/Hello.jsx
import React from 'react';
import { renderRoutes } from 'react-router-config';
function Hello({ route, name }) {
// NOTE: "route" isn't always defined. See TypeScript example below.
return (
<div>
Hello, {name}!
{route && renderRoutes(route.routes)}
</div>
);
}
export default Hello;
TypeScript equivalent: awesome-project/src/Hello.tsx
import React from 'react';
import { RouteConfigComponentProps, renderRoutes } from 'react-router-config';
interface HelloProps {
name: string;
}
// Explicitly expect "route" props to be passed to this component.
// Wrap the existing "HelloProps" with "RouteConfigComponentProps" to apply the predicted values.
export const Hello = function Hello({ route, name }: RouteConfigComponentProps<HelloProps>) {
const childRoutes = route
? renderRoutes(route.routes)
: undefined;
return (
<div>
Hello, {name}!
{childRoutes}
</div>
);
}
API
RouteConfig
This is actually a type definition from @types/react-router-config
and is not exported by this library if your project uses TypeScript (you should rely on the aforementioned typings for that), but is included here for reference purposes since it is also used very frequently by this library. JavaScript-only developers are also welcome to use to these definitions as guidance.
export interface RouteConfig {
key?: React.Key;
location?: Location;
component?: React.ComponentType<RouteConfigComponentProps<any>> | React.ComponentType;
path?: string;
exact?: boolean;
strict?: boolean;
routes?: RouteConfig[];
render?: (props: RouteConfigComponentProps<any>) => React.ReactNode;
[propName: string]: any;
}
For further reading, see the react-router-config
documentation.
patchTreesRoutePath(trees?: RouteConfig[]): RouteConfig[] | undefined
Prefixes the paths of an array of RouteConfig
objects and their nested RouteConfig
objects with the paths of their parents. If no RouteConfig
array has been provided, the function will return undefined
.
The patching is recursive; once you call this function on the root of the tree
, all of its leaves will also be patched.
That means if your configuration looks like...
import { patchRouteConfigPaths } from '@mamawosh/router-config';
const oldRoutes = [
{
path: '/level-1',
component: LevelOne,
routes: [
{
path: '/level-2',
component: LevelTwo,
routes: [
{
path: '/level-3'
component: LevelThree,
},
],
},
],
},
];
const newRoutes = patchRouteConfigPaths(oldRoutes);
... then this function generates a result the equivalent of:
const newRoutes = [
{
path: '/level-1',
component: LevelOne,
routes: [
{
path: '/level-1/level-2',
component: LevelTwo,
routes: [
{
path: '/level-1/level-2/level-3'
component: LevelThree,
},
],
},
],
},
];
This function was built on the expectation that you will use react-router-config
's renderRoutes(routes?: RouteConfig[])
function verbatim. If, instead, you prefer to prefix paths starting at specific node levels, use the next function, patchTreeRoutePath(tree: RouteConfig): RouteConfig
.
patchTreeRoutePath(tree: RouteConfig): RouteConfig
Prefixes the path of a RouteConfig
object. Unlike the previous function, the tree
parameter is required.
The patching is recursive; once you call this function on the root of the tree
, all of its leaves will also be patched.
import { patchRouteConfigPath } from '@mamawosh/router-config';
const oldRoute = {
path: '/level-1',
component: LevelOne,
routes: [
{
path: '/level-2',
component: LevelTwo,
},
],
};
const newRoute = patchRouteConfigPaths(oldRoute);
... is the equivalent of:
const newRoute = {
path: '/level-1',
component: LevelOne,
routes: [
{
path: '/level-1/level-2',
component: LevelTwo,
},
],
};
<RouteConfigComponent />
Renders the subroutes without adding any extra content.
import { RouteConfigComponent } from '@mamawosh/router-config';
const tree = [
{
path: '/level-1',
component: RouteConfigComponent, // Should render level two without any additional content.
routes: [
{
path: '/level-2',
component: RouteConfigComponent, // Should render level three without any additional content.
routes: [
{
path: '/level-3'
component: LevelThree, // Renders a string which says "Level Three".
},
],
},
],
},
];
Use of this component in other components is not encouraged, but is perfectly valid, as long as this component receives all values in RouteConfigComponentProps
(which extends RouteComponentProps
):
Here's a rough equivalent of RouteConfigComponentProps
:
import * as H from 'history';
// NOTE:
// This is a rough equivalent of RouteConfigComponentProps<{}>.
// Always refer to the source materials for a better understanding!
interface RouteConfigComponentProps<Params extends { [K in keyof Params]?: string } = {}, C extends StaticContext = StaticContext, S = H.LocationState> {
// "Params" refers to the kinds of parameters you're expecting the URL to provide, e.g. /:userId, /:addressId, etc.
history: H.History;
location: H.Location<S>;
match: match<Params>;
staticContext?: C;
route?: RouteConfig; // Contains subroutes. See the `RouteConfig` sample earlier!
}
Here's how you can use RouteConfigComponent
:
import React from 'react';
import { RouteConfigComponent, RouteConfigComponentProps } from 'react-router-config';
interface SomeComponentProps {
// Your custom props here.
}
// Explicitly expect "route" props to be passed to this component.
// Wrap the existing "HelloProps" with "RouteConfigComponentProps" to apply the predicted values.
export const SomeComponent = function SomeComponent({
history, // Pass this value to <RouteConfigComponent>!
location, // Pass this value to <RouteConfigComponent>!
match, // Pass this value to <RouteConfigComponent>!
staticContext, // Pass this value to <RouteConfigComponent>!
route, // Pass this value to <RouteConfigComponent>!
name,
}: RouteConfigComponentProps<SomeComponentProps>) {
const routeConfigComponentProps = RouteConfigComponentProps<{}> = {
history,
location,
match,
staticContext,
route,
};
return (
<div>
Hello, {name}!
<RouteConfigComponent {...routeConfigComponentProps} />
</div>
);
}
We recommend that you simply use renderRoutes(routes?: RouteConfig[])
instead.
Develop
Install all dependencies
The /example
folder uses Yarn, not NPM. We recommend that you install Yarn, too. When you run npm install
, the scripts automatically download everything you need for both the library and its /example
. If you need to run this yourself, read on.
Install all dependencies:
npm run install-all
This is the equivalent of...
npm install
cd example
yarn
cd ..
Run the example as you edit
To run the example, you'll need to build the distributable copy of the library's source code first as the example depends on the local copy, rather than the NPM registry copy. In order to make things easy, we made a convenience command available:
npm run develop
Every change you make & save to the source code will update the distributable copy in the /example/src/@mamawosh/router-config
folder. Do not edit the code there.
In order to use this library in the example provided, import it like you would with a normal NPM module:
import { patchTreesRoutePath } from '@mamawosh/router-config';
const routes = patchTressRoutePath([
// Write your implementations...
]);
Build
If you need a distributable copy for any particular reason, you can generate it through the source code.
Run the following command to create a distributable copy:
npm run build
The distributables will be produced in the /dist
folder of your local copy of this library.
You can also tell the script to rebuild the distributables every time you edit & save your code:
npm run build-watch
However, for such cases, we recommend the Develop instructions as they require fewer steps.
Test
This library is tested using Jest.
Run the following command to test the code:
npm test
Publish
Update the version of the package using version-bump-prompt
(see online instructions). Here's an example of a patch update in beta mode:
bump patch --beta
You can also update the versions manually, but it is not recommended.
Login & verify that the update shows no problems during the dry run:
npm login
npm publish --access public --dry-run
Run the following once all is good:
npm publish --access public