React Router Route Higher-Order Component
Inspired by type-route
The binding for react-router that provides a new way of declaring react-router route with path params and query params validation and typescript support
Use cases
-
⚗️ Declare route inside a component -
⛔️ Declare validation for route path and query params -
🚀 Generate links for a route with necessary params -
💻 Get suggestions in IDE
Documentation
Installation
npm i react-router-hoc react-router
The binding helps you to declare a react-router route in the component itself or in a container. Preferably this is created to define a route in the component which helps you to contain everything in one file.
Usage
Route declaration with path and query params validation
import { Route } from "react-router-hoc";
const DashboardRoute = Route(
{
role: Route.params.oneOf("customer", "employee"),
region: Route.params.string,
storage: Route.params.number.optional,
page: Route.query.number,
range: Route.query.oneOf('day', 'week'),
gangs: Route.query.array(Route.query.oneOf('ballas', 'grove_street', 'lost_santos'))
},
({ role, region, storage }) => `/dashboard/${role}/${region}/${storage}`
);
// Example props type
type Props = {
user?: User;
};
export default DashboardRoute<Props>(
({
user,
match: {
params: { role, region, storage },
/* Set default query params if it does not match the validation rule */
query: { page = 1, range = 'day', gangs = ['ballas']}
},
}) => {
return; /** template */
}
);
Use it as a regular route component in the router
<Router>
<Dashboard user={user} />
</Router>
<Router>
<Switch>
<Home exact />
<Dashboard user={user} />
<NotFound />
</Switch>
</Router>
Links generation
Generate links object
// App.tsx
import { getLinks } from 'react-router-hoc'
import Dashboard from 'Dashboard'
import Search from 'Search'
import Article from 'Article'
import Home from 'Home'
export const links = getLinks({
Dashboard,
Search,
Article,
Home
})
// Search.tsx
import { links } from 'App'
import { Link } from 'react-router-dom'
import { Route } from 'react-router-hoc'
export default Route(`/search`)(() => (
<section>
<Link
/* Provide all path and query params as a single object argument */
to={links.Dashboard({
role: "customer",
region: "Staryi Sambir",
range: "week",
page: 3
})}
/>
</section>
))
Generate link to the route
import Dashboard from 'Dashboard'
<Router>
<Link to={Dashboard.link({ role: 'customer', region: 'Staryi Sambir'})}>
</Router>
Get link to the same component
If you want to change params and query params for the same route, you can use the link function from props to generate the necessary link
import { Route } from "react-router-hoc"
export default Route(
{ city: Route.query.string },
"/search"
)(({ link }) => {
return (
<section>
<input onBlur={event => link({ city: event.target.value })} />
{city}
</section>
)
})
HOCs composition
compose
to combine any HOCs with Route HOC
Use import { ProtectedRoute } from './your-awesome-protected-route-hoc'
import { Route, compose } from 'react-router-hoc'
const SearchRoute = compose(
ProtectedRoute,
Route({
city: Route.params.string,
page: Route.query.number
}, ({ city}) => `/search/${city}`)
)
export default SearchRoute(() => /* template /*)
Route declaration with only path params
import { Route } from "react-router-hoc"
const SearchRoute = Route(
{ city: Route.params.string.optional },
({ city }) => `/search/${city}`
)
export default SearchRoute(
({
match: {
params: { city }
}
}) => {
return /** template */
}
)
Route declaration with only query params
import { Route } from "react-router-hoc"
const SearchRoute = Route(
{ age: Route.query.number },
`/search`
)
export default SearchRoute(
({
match: {
/* Set default query param if necessary */
query: { age = 18 }
}
}) => {
return /** template */
}
)
Route declaration without params
import { Route } from "react-router-hoc";
const HomeRoute = Route(`/home`);
export default HomeRoute(() => {
return; /** template */
});
Curring route declaration
import { Route } from 'react-router-hoc'
export default Route(`/home`)(() => /** template */)
Empty route declaration (for compatibility with react-router)
import { Route } from 'react-router-hoc'
export default Route(() => /** template */)
API
Validation rules
Route HOC provides a way to declare validation rules for path and query params, once you apply the rule it will influence route matching.
Path params
role: Route.params.oneOf("customer", "employee"),
region: Route.params.string,
storage: Route.params.number,
hash: Route.params.regex(/[0-9a-fA-f]{40}/),
optional: Route.params.string.optional
Query params
role: Route.query.oneOf("customer", "employee"),
region: Route.query.string,
storage: Route.query.number,
hash: Route.query.regex(/[0-9a-fA-f]{40}/),
Rule | Match | Example |
---|---|---|
Route.params.string |
Match any value |
/:any (/228 -> '228' will be converted to a string) |
Route.params.number |
Match only numbers |
/:number ('3078' -> 3078 will be converted to a number, /foo won't match the route) |
Route.params.oneOf |
Match one of variants |
/customer or /employee
|
Route.params.regex |
Match a regex |
regex(/[0-9a-fA-f]{40}/) (ca82a6dff817ec66f44342007202690a93763949 match commit hash) |
Route.params.optional |
Make a rule optional |
/any or /
|
Route.query.string |
Match any value |
?param=anyValue (Any value is propagated as a string) |
Route.query.number |
Match only numbers |
/params=6 ('3078' -> 3078 will be converted to a number, if no number is provided, the value will be undefined ) |
Route.query.oneOf |
Match one of variants |
?param=customer or ?param=employee , if no one of these value is provided, the value will be undefined
|
Route.query.regex |
Match a regex |
regex(/[0-9a-fA-f]{40}/) (?hash=ca82a6dff817ec66f44342007202690a93763949 match commit hash, if the provided value doesn't match the pattern, undefined will be set to the query param) |
Route.query.array(Route.query[rule]) |
Match array of values validate by the route |
?gang=ballas&gang=mafia , will match an array of values that are validated by the other rules, if no array is provided it will be undefined , if an array doesn't contain a value that matches validation, it won't appear in an array |