README
react-hook-guard
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 Hook Guards provides angular-like to implement the React Router, allowing you to perform complex logics in various hooks to guards your router.
Table of Contents
Requirements
This package has the following peer dependencies:
React v17.0.2+
React Router DOM v5.2.0+
Installation
With npm:
$ npm install react-hook-guard
With yarn:
$ yarn add react-hook-guard
Then with a module bundler like webpack, use as you would anything else:
// using ES6 modules
import {RouterOutlet} from 'react-hook-guard';
// using CommonJS modules
const RouterOutlet = require('react-hook-guard').RouterOutlet;
Basic usage
Here is a very basic example of how to use React Hook Guard.
// App.tsx
import {Route, Routes, WithRouteProps, RouterOutlet} from 'react-hook-guard';
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'}
},
// 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}/>;
}
Customize fallback components
By default, react-hook-guard 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-hook-guard';
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-hook-guard 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-hook-guard will auto ignore the global if `the last route in a `chilren` array`
// is the same pathMatch with the global
matchAllRoute,
});
// More codes here