diff --git a/README.md b/README.md index 445dac9..6d55358 100644 --- a/README.md +++ b/README.md @@ -288,6 +288,43 @@ export default App Now our component will display the todo list only when it has loaded. +### Common use case: displaying a component when a route is selected + +In most applications, there are menus that select components based on the user +selecting a sub-application. To display components whose sole display criteria is +the selection of a route, use a `RouteToggle` + +```javascript +import RouteToggle from 'react-redux-saga-router' + +const TodosRoute = RouteToggle('todos') +``` + +In this way, you can display several components scattered around a layout template +that are route-specific without having to make a new layout template just for that route, +or doing any strange contortions. + +A `RouteToggle` accepts all the arguments of Toggle afterwards: + +```javascript +import RouteToggle from 'react-redux-saga-router' + +const TodosRoute = RouteToggle('todos', state => state.whatever === 'hi') +``` + +The example above will only toggle if the todos route is active and the `whatever` +portion of state is equal to 'hi' + +A `RouteToggle` can be thought of +as a simpler version of this source code: + +```javascript +import Toggle from 'react-redux-saga-router/Toggle' +import { matchedRoutes } from 'react-redux-saga-router/selectors' + +const TodosRoute = Toggle(state => matchedRoutes(state, 'todos')) +``` + ### Available selectors for Toggles The following selectors are available for use with Toggles. import as follows: @@ -305,6 +342,27 @@ import Toggle from 'react-redux-saga-router/Toggle' export Toggle(state => selectors.matchedRoute(state, 'routename')) ``` +`matchedRoute` accepts a single route name, or an array of route names to match. +By default, it matches on any route. To enable strict matching (all routes must match) +pass in true to the third parameter of matchedRoute + +```javascript +import * as selectors from 'react-redux-saga-router/selectors' +import Toggle from 'react-redux-saga-router/Toggle' + +export Toggle(state => selectors.matchedRoute(state, ['route', 'subroute'], true)) +``` + +This is useful for strict matching of a sub-route path. + +Note that a convenience Toggle, `RouteToggle` exists to match a route: + +```javascript +import RouteToggle from 'react-redux-saga-router/RouteToggle' + +export RouteToggle('routename', state => otherconditions()) +``` + This selector returns true if the route specified by `'routename'` is active #### noMatches @@ -407,9 +465,10 @@ We need 2 things: ```javascript import * as selectors from 'react-redux-saga-router/selectors' import Toggle from 'react-redux-saga-router/Toggle' +import RouteToggle from 'react-redux-saga-router/RouteToggle' -const AboutToggle = Toggle(state => selectors.matchedRoute('about')) -const UsersToggle = Toggle(state => selectors.matchedRoute('users') || Toggle(state => selectors.matchedRoute('user'))) +const AboutToggle = RouteToggle('about') +const UsersToggle = RouteToggle(['users', 'user']) const SelectedUserToggle = Toggle(state => !!state.users.selectedUser, state => usersLoaded(state) && state.users.user[state.users.selectedUser]) const NoMatchToggle = Toggle(state => selectors.noMatches(state)) diff --git a/RouteToggle.js b/RouteToggle.js new file mode 100644 index 0000000..1183f2b --- /dev/null +++ b/RouteToggle.js @@ -0,0 +1 @@ +module.exports = require('./lib/RouteToggle.js') diff --git a/package.json b/package.json index 2752054..e8ae921 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-redux-saga-router", - "version": "0.6.2", + "version": "0.7.0", "description": "elegant powerful routing based on the simplicity of storing url as state", "main": "lib/index.js", "directories": { diff --git a/src/RouteToggle.jsx b/src/RouteToggle.jsx new file mode 100644 index 0000000..674c975 --- /dev/null +++ b/src/RouteToggle.jsx @@ -0,0 +1,9 @@ +import Toggle from './Toggle' +import * as selectors from './selectors' + +export default function RouteToggle(route, othertests = null, loading = undefined, + componentMap = {}, debug = false) { + return Toggle(state => (selectors.matchedRoute(state, route) && + (othertests ? othertests(state) : true) + ), loading, componentMap, debug) +} diff --git a/src/selectors.js b/src/selectors.js index 5ad9a82..ca93a4c 100644 --- a/src/selectors.js +++ b/src/selectors.js @@ -1,5 +1,10 @@ -export function matchedRoute(state, name) { - return state.routing.matchedRoutes.some(route => route === name) +export function matchedRoute(state, name, strict = false) { + if (Array.isArray(name)) { + const matches = state.routing.matchedRoutes.filter(route => name.includes(route)) + if (strict) return matches.length === name.length + return !!matches.length + } + return state.routing.matchedRoutes.includes(name) } export function noMatches(state) { diff --git a/test/RouteToggle.test.js b/test/RouteToggle.test.js new file mode 100644 index 0000000..abb5acb --- /dev/null +++ b/test/RouteToggle.test.js @@ -0,0 +1,116 @@ +import React from 'react' +import { connectToggle } from '../src/Toggle' + +import RouteToggle from '../src/RouteToggle' +import { renderComponent, connect } from './test_helper' + +describe('RouteToggle', () => { + const Component = props => ( // eslint-disable-next-line +