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
- ⚗️ 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
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.
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 */
}
);
<Router>
<Dashboard user={user} />
</Router>
<Router>
<Switch>
<Home exact />
<Dashboard user={user} />
<NotFound />
</Switch>
</Router>
// 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>
))
import Dashboard from 'Dashboard'
<Router>
<Link to={Dashboard.link({ role: 'customer', region: 'Staryi Sambir'})}>
</Router>
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>
)
})
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 /*)
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 */
}
)
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 */
}
)
import { Route } from "react-router-hoc";
const HomeRoute = Route(`/home`);
export default HomeRoute(() => {
return; /** template */
});
import { Route } from 'react-router-hoc'
export default Route(`/home`)(() => /** template */)
import { Route } from 'react-router-hoc'
export default Route(() => /** template */)
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 |