Implement react-route-dom with configuration
Simple guard routers with functions
Flexible guard routers with react hooks
Easily to make a multiple layout react app
Relative mode supported for config route
React Router Map provides angular-like to implement the React Router, allowing you to perform complex logics in various hooks to guards your router.
This package has the following peer dependencies:
-
React v17.0.2+
-
React Router DOM v5.2.0+
With npm:
$ npm install react-router-map
With yarn:
$ yarn add react-router-map
Then with a module bundler like webpack, use as you would anything else:
// using ES6 modules
import {RouterOutlet} from 'react-router-map';
// using CommonJS modules
const RouterOutlet = require('react-router-map').RouterOutlet;
Here is a very basic example of how to use React Hook Guard.
// App.tsx
import {Route, Routes, WithRouteProps, RouterOutlet} from 'react-router-map';
import {lazy, useContext, useEffect, useState} from 'react';
import {useSelector} from 'react-redux';
import {useHistory} from 'react-router';
function WithoutNavbarLayout({...props}: any) {
return (
<div className={'container'}>
{/* Pass all other props here to extends the relative path from parent */}
<RouterOutlet {...props}/>
</div>
);
}
function WithNavbarLayout({...props}: any) {
return (
<div className={'container'}>
<nav className={'nav-bar'}>
<Link to={'/dashboard'}>Dashboard</Link>
<Link to={'/info'}>Info</Link>
</nav>
<RouterOutlet {...props} />
</div>
);
}
const fakeUser = {name: 'Smith', role: 'admin'};
function useAuthenticationGuard() {
// Retrieve auth data from any where, with any hooks to authentication
// const user = useSelector(state => state.user);
// const user = useContext(userContext);
const [user] = useState(fakeUser);
return !!user;
}
function useGuestGuard() {
// Share the same resource data with authentication guard
const [user] = useState(fakeUser);
const history = useHistory();
// use history to intercept when user logged in.
// Should wrap the code in useEffect hooks to avoid some render problems.
useEffect(() => {
if (user) {
history.push('/dashboard');
}
}, [user, history]);
return !user;
}
function useAuthorizationGuard(route: Route) {
const [user] = useState(fakeUser);
// Use role from route config to dynamic authorization
return !!user && route.data?.role && user.role === route.data?.role;
}
const appRoutes: Routes = [
{
path: 'auth', // Please, do not write root route such as '/auth' to use relativeMode
component: WithoutNavbarLayout,
canActivate: [useGuestGuard],
children: [
{
path: 'login',
component: lazy(() => import('./features/auth/login')),
data: {title: 'Login'},
name: 'login'
},
// redirect relative with the parent path, you can use `absoluteRedirectTo` property instead for absolute path
{path: '', redirectTo: 'login', exact: true}
]
},
{
path: '',
component: WithNavbarLayout,
canActivate: [useAuthenticationGuard],
// Auto extend the guard to `canActivate` of every children
// Because each child route will have different role requirements
canActivateChild: [useAuthorizationGuard],
children: [
{
path: 'dashboard',
component: lazy(() => import('./features/dashboard')),
data: {role: 'admin'}
},
{path: 'info', component: lazy(() => import('./features/info'))},
{path: '', redirectTo: 'dashboard', exact: true}
]
},
];
function App() {
// Ignore relativeMode property if you want to use absolute route paths
return <RouterOutlet routes={appRoutes} relativeMode={true}/>;
}
By default, react-router-map will not redirect you to any other route, if the guard functions return false, a default empty component will be returned
function RouteFallback() {
return (
<div/>
);
}
export default RouteFallback;
To customize this, please create your own custom component, and then config it in index.tsx
or index.js
file.
function SuspenseFallback() {
return (
<div>
Loading...
</div>
);
}
function CantActivateFallback() {
return (
<div>
You can not access this page
</div>
);
}
function NotFound() {
return (
<div>
404 Not found
</div>
);
}
const matchAllRoute = {
path: '',
component: NotFound,
}
// index.tsx
import reactHookGuard from 'react-router-map';
reactHookGuard.config({
// Suspense component will be use to Suspense the lazyload components
SuspenseFallback,
// Can't Activate component will be use when the route can not be access
CantActivateFallback,
// This config allow you config a global page when not found any matches routes
// Under the hood, react-router-map will auto create an extra relative route like the configured to each RouterOutlet
// So if you want to use specific matchAllRoute for specific RouterOutlet
// just provide the last route with the same `path` and `exact` with the global that you configured,
// react-router-map will auto ignore the global if `the last route in a `chilren` array`
// is the same pathMatch with the global
matchAllRoute,
});
// More codes here
By default, react-router-dom 5 doesn't support relative <Link />
component, this package will support the relative link with current route match.
import {RelativeLink} from "react-router-map";
const HaveRelativeLinkComponent = () => {
// E.g: The path is /app/:foo/:bar
// And the url is /app/my-company/departments
// The RelativeLink will allow us to navigate to /app/my-company/departments/create
return (
<RelativeLink to={'create'} />
)
}
We also provide another hook for navigating relatively
1. Using hook
import {useNavigate} from "react-router-map";
import {useEffect} from "react";
const HaveNavigateComponent = () => {
// E.g: The path is /app/:foo/:bar
// And the url is /app/my-company/departments
// The navigate will allow us to navigate to /app/my-company/departments/create
const navigate = useNavigate();
useEffect(() => {
navigate('create');
}, [navigate]);
}
2. Using browserHistory
// index.tsx
import React from "react";
import ReactDOM from "react-dom";
import { Router } from "react-router";
import { createBrowserHistory } from "history";
import App from './App';
export const browserHistory = createBrowserHistory();
ReactDOM.render(
<React.StrictMode>
<Router history={browserHistory}>
<App />
</Router>
</React.StrictMode>,
document.getElementById("root")
);
...
// App.tsx
import { browserHistory } from '..';
function App() {
return (
<div>
<button onClick={() => browserHistory.push('/example')}>
Navigate
</button>
</div>
);
}
export default App;
3. Using Navigator Helper Class
import { Navigator } from 'react-router-map';
...
return (
<div>
<button onClick={() => Navigator.push('yourRouteName')}>
Navigate
</button>
</div>
);