a guard middleware for react-router v6
npm install react-router-guarded-routes> English | 简体中文
A guard middleware for react-router v6, inspired by react-router-guards.
- React-Router-Guarded-Routes
- Install
- Usage
- Basic
- Guarding
- API
- Types
- Components
- GuardConfigProvider
- Props
- Setup
- GuardProvider
- Props
- Setup
- GuardedRoutes
- Props
- Setup
- GuardedRoute
- Props
- Setup
- Hooks
- useGuardedRoutes
- Props
- Setup
``sh`
npm install react-router-guarded-routes react-router --saveor
yarn add react-router-guarded-routes react-routeror
pnpm add react-router-guarded-routes react-router
Provides GuardConfigProvider in BrowserRouter, and you can use it like react-router (compatible with the apis of react-router).
`tsx
import { BrowserRouter } from 'react-router-dom'
import {
GuardConfigProvider,
GuardedRoute,
GuardedRoutes,
} from 'react-router-guarded-routes'
export default function App() {
return (
Use hooks:
`tsx
import {
GuardedRouteObject,
useGuardedRoutes,
} from 'react-router-guarded-routes'const routes: GuardedRouteObject[] = [
{ path: '/foo', element:
foo },
{
path: '/bar/*',
element: bar,
children: [{ path: '/bar/baz', element: baz }],
},
]function Routes() {
return {useGuardedRoutes([routes])}
}
export default function App() {
return (
)
}
`$3
You can provide
GuardProvider with multiple guards middleware for route guarding, GuardProvider can receive an array of guards and a fallback element (can be used to load loading state).`tsx
import { BrowserRouter } from 'react-router-dom'
import {
GuardConfigProvider,
GuardedRoute,
GuardedRoutes,
GuardMiddleware,
GuardProvider,
} from 'react-router-guarded-routes'const logGuard: GuardMiddleware = (to, from, next) => {
console.log(to) // { location, matches, route }
console.log(from)
next() // call next function to run the next middleware or show the route element, it accepts the same parameters as navigate (useNavigate()) and behaves consistently.
}
// you can use object to determine whether you need to register middleware
const barGuard: GuardMiddleware = {
handler: (to, from, next) => {
console.log('bar')
next()
},
register: (to, from) => {
// only matched with
/bar can be executed.
if (to.location.pathname.startsWith('/bar')) {
return true
}
return false
},
}const guards = [logGuard, barGuard]
export default function App() {
return (
{/ Guard all routes below. /}
loading...
Of course, you can also set up separate fallbacks and guards for each route.
`tsx
import { BrowserRouter, Outlet } from 'react-router-dom'
import {
GuardConfigProvider,
GuardedRoute,
GuardedRoutes,
GuardMiddleware,
GuardProvider,
} from 'react-router-guarded-routes'const logGuard: GuardMiddleware = (to, from, next) => {
console.log(to, from)
next()
}
const fooGuard: GuardMiddleware = (to, from, next) => {
console.log('foo')
next()
}
const guards = [logGuard]
const fooGuards = [fooGuard]
export default function App() {
return (
loading...} guards={guards}>
fallback={loading foo...}
guards={fooGuard}
element={foo}
path="/foo"
/>
element={
bar
}
path="/bar/*"
>
baz} path="/bar/baz" />
)
}
`You can also call
next.ctx('ctx value') to transfer contextual information, and get it by ctxValue in the next guard middleware. The guard middleware is executed from outside to inside, left to right.`tsx
fallback={loading...}
guards={(to, from, next) => {
next.ctx('ctx value')
}}
>
guards={(to, from, next, { ctxValue }) => {
console.log(ctxValue) // ctx value
next()
}}
element={foo}
path="/foo"
/>
`And call
next.end() to ignore remaining middleware.`tsx
fallback={loading...}
guards={
((to, from, next) => {
next.end()
},
() => {
console.log('will not be called')
})
}
>
guards={() => {
console.log('will not be called')
}}
element={foo}
path="/foo"
/>
`API
$3
`ts
import React from 'react'
import {
Location,
NavigateFunction,
RouteMatch,
RouteObject,
} from 'react-router'
import { ReplacePick } from 'types-kit'export interface GuardedRouteConfig {
guards?: GuardMiddleware[]
fallback?: React.ReactNode
[props: PropertyKey]: any
}
export type GuardedRouteObject = RouteObject &
GuardedRouteConfig & {
children?: GuardedRouteObject[]
}
export interface NextFunction extends NavigateFunction {
(): void
ctx: (value: T) => void
end: () => void
}
export interface GuardedRouteMatch
extends Omit, 'route'> {
route: GuardedRouteObject
}
export interface ToGuardRouteOptions {
location: Location
matches: GuardedRouteMatch[]
route: GuardedRouteObject
}
export interface FromGuardRouteOptions
extends ReplacePick<
ToGuardRouteOptions,
['location', 'route'],
[
ToGuardRouteOptions['location'] | null,
ToGuardRouteOptions['route'] | null
]
> {}
export interface ExternalOptions {
ctxValue: T
injectedValue: I
}
export type GuardMiddlewareFunction = (
to: ToGuardRouteOptions,
from: FromGuardRouteOptions,
next: NextFunction,
externalOptions: ExternalOptions
) => Promise | void
export type GuardMiddlewareObject = {
handler: GuardMiddlewareFunction
register?: (
to: ToGuardRouteOptions,
from: FromGuardRouteOptions
) => Promise | boolean
}
export type GuardMiddleware =
| GuardMiddlewareFunction
| GuardMiddlewareObject
`$3
#### GuardConfigProvider
The
GuardConfigProvider has configuration about routing, should not be used more than one in an app, make sure it's at the topmost level inside the Router (BrowserRouter and HashRouter).And it provides APIs for whether to run guard middleware and whether to display the fallback element:
##### Props
`tsx
import React from 'react'export interface GuardConfigProviderProps {
enableGuard?: (
location: ToGuardRouteOptions,
prevLocation: FromGuardRouteOptions
) => Promise | boolean
enableFallback?: (
location: ToGuardRouteOptions,
prevLocation: FromGuardRouteOptions
) => boolean
children: React.ReactNode
}
`| Prop | Optional | Default | Description |
| ---------------- | :------: | :------------------------------------------------------------: | --------------------------------------- |
|
enableGuards | Yes | (to, from) => to.location.pathname !== from.location?.pathname | whether to run guard middleware |
| enableFallback | Yes | () => true | whether to display the fallback element |##### Setup
`tsx
import { BrowserRouter } from 'react-router-dom'
import { GuardConfigProvider } from 'react-router-guarded-routes'
export default function App() {
return (
{
// routes
}
)
}
`#### GuardProvider
It provides public fallback element and guard middleware for
GuardedRoute.##### Props
`tsx
import React from 'react'export interface GuardProviderProps {
fallback?: React.ReactElement
useInject?: (
to: ToGuardRouteOptions,
from: FromGuardRouteOptions
) => Record
guards?: GuardedRouteConfig['guards']
children: React.ReactNode
}
`| Prop | Optional | Default | Description |
| ----------- | :------: | :-----: | ------------------------------------------------------------------------------------------------------------------------------------------ |
|
fallback | Yes | | a fallback element to show when a GuardedRoute run guard middleware |
| useInject | Yes | | an injected value (React hooks can be used) for guard middleware to use, will be automatically merged the values of nested GuardProvider |
| guards | Yes | | the guards to set for routes inside the GuardProvider |##### Setup
`tsx
import { BrowserRouter } from 'react-router-dom'
import {
GuardConfigProvider,
GuardedRoute,
GuardedRoutes,
GuardMiddleware,
GuardProvider,
} from 'react-router-guarded-routes'const logGuard: GuardMiddleware = (to, from, next) => {
console.log(to, from)
next()
}
export default function App() {
return (
loading...} guards={[logGuard]}>
foo} path="/foo" />
)
}
`Use nested GuardProvider:
`tsx
loading...}>
foo} path="/foo" />
loading2...}>
element={
bar
}
path="/bar/*"
>
baz} path="/bar/baz" />
`Inject value:
`tsx
import { createContext } from 'react'
import { BrowserRouter } from 'react-router-dom'
import {
GuardConfigProvider,
GuardedRoute,
GuardedRoutes,
GuardProvider,
} from 'react-router-guarded-routes'export const AuthContext = createContext({
isLogin: false,
})
export function useAuth() {
return useContext(AuthContext)
}
export default function App() {
return (
fallback={loading...}
useInject={useAuth}
guards={[
(to, from, next, { injectedValue }) => {
console.log(injectedValue) // { isLogin: false }
next()
},
]}
>
foo} path="/foo" />
)
}
`#### GuardedRoutes
The
GuardedRoutes component acts as a replacement for the default Routes component provided by React Router.##### Props
`tsx
import { RoutesProps } from 'react-router'export interface GuardedRoutesProps extends RoutesProps {}
`##### Setup
`tsx
foo} path="/foo" />
`#### GuardedRoute
The
GuardedRoute component acts as a replacement for the default Route component provided by React Router, allowing for routes to use guard middleware and accepting the same props as regular Route.##### Props
`tsx
import { Route } from 'react-router'
type RouteProps = Parameters[0]export type GuardedRouteProps = RouteProps & GuardedRouteConfig
`The following table explains the guard-specific props for this component.
| Prop | Optional | Default | Description |
| ---------- | :------: | :-----: | ---------------------------------------------------------------------------------------------------------------------------------- |
|
fallback | Yes | | a fallback element to show when a GuardedRoute run guard middleware. (it will override the fallback provided by GuardProvider) |
| guards | Yes | | the guards to set for the route |##### Setup
`tsx
element={foo}
path="/foo"
fallback={loading...}
guards={[
(to, from, next) => {
next()
},
]}
/>
`$3
#### useGuardedRoutes
The
useGuardedRoutes hook acts as a replacement for the default useRoutes hook provided by React Router, and additionally provides fallback and guards properties for each member.##### Props
`tsx
import { useRoutes } from 'react-router'type LocationArg = Parameters[1]
export function useGuardedRoutes(
guardedRoutes: GuardedRouteObject[],
locationArg?: LocationArg
): ReturnType
`##### Setup
`tsx
import {
GuardedRouteObject,
useGuardedRoutes,
} from 'react-router-guarded-routes'
const routes: GuardedRouteObject[] = [
{
path: '/foo',
element: foo,
fallback: loading foo...,
guards: [(to, from, next) => next()],
},
]function Routes() {
return <>{useGuardedRoutes(routes)}>
}
export default function App() {
return (
loading...}>
)
}
``