Skip to content

svenit/react-router-map

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

6 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

react-router-map

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.

Table of Contents

Requirements

This package has the following peer dependencies:

Installation

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;

Basic usage

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}/>;
}

Customize fallback components

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

Relative Link support

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>
);

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published