React Router Interceptor
Execute callback functions before transitioning to a route. Interceptors support Promise based awaiting, history powered state caching, and redirecting.
React Router Interceptor removes the need for skeletons and loading spinners by loading your page state before rendering it. State returned by interceptors is accessible directly from the route component, allowing for an efficient page loading workflow.
Installation
Install the package using NPM
npm install react-router-interceptor
Basic setup
React Router Interceptor currently only supports routes defined using useRoutes
. You may add an interceptor function inside any route object, including child routes.
const routes: InterceptableRouteObject[] = [
{
path: '/example',
element: <ExamplePage />,
intercept: () => {
}
}
];
Your application must use the InterceptableRouter
component in which your routes must be passed. It is recommended to pass a render prop function as child to the router in order to conditionally display a loading page for the initial interceptor.
const element = document.getElementById('root')!
const root = createRoot(element);
root.render(
<InterceptableRouter routes={routes} >
{ loaded => {
return loaded
? <App routes={routes} />
: <Loading />
}}
</InterceptableRouter>
);
React Router Interceptor supports a variety of ways to integrate intercepting into your application, these are described further down.
Fetching data
Interceptors allow returning an object or Promise to expose state to the page component. The page will be delayed from rendering until the interceptor Promise has been resolved.
The interceptor function is passed the route parameters and the exact path being transitioned to.
const routes: InterceptableRouteObject[] = [
{
path: '/profile/:id',
element: <ExamplePage />,
intercept: async (params, to) => {
const profile = await fetchUserProfile(params.id);
return {
profile
};
}
}
];
You can retrieve the returned state by using the useIntercepted()
hook.
export function ExamplePage() {
const { profile } = useIntercepted();
return (
);
}
Redirecting
Interceptors can return a path string to redirect the request to a different route.
const routes: InterceptableRouteObject[] = [
{
path: '/profile/:id',
element: <ExamplePage />,
intercept: async (params) => {
const profile = await fetchUserProfile(params.id);
if (!profile.isActive) {
return '/';
}
return {
profile
};
}
}
];
Handling initial render
We cannot automatically delay the loading of the page until the initial page's interceptor has completed. We instead provide multiple ways to integrate the possibility of a loading page into your application.
Render prop
The easiest way to display a loading page is by passing a render prop function as child to the
InterceptableRouter
component.
<InterceptableRouter routes={routes}>
{ loaded => {
return loaded
? <App routes={routes} />
: <Loading />
}}
</InterceptableRouter>
External store
You can also choose to store the loading state in an external store (e.g. Redux). This has the benefit
of making the loading state directly available to your entire application. This means you must handle
the rendering of a loading page inside your App component.
<InterceptableRouter
routes={routes}
onAfterIntercept={() => store.dispatch(reducers.setLoaded(true)))}
>
<App routes={routes} />
</InterceptableRouter>
Example
An example implementation can be found here.
Recommendation
If you're looking for further react router enhancements, check out our other package react-router-tree which allows you to parse your routes automatically from a directory structure.
Vindigo
This package was originally developed for use in Vindigo, a free and open source task planner.
License
react-router-interceptor is licensed under MIT
Copyright (c) 2022-present, Starlane Studios