Coder Social home page Coder Social logo

upstatement / react-router-guards Goto Github PK

View Code? Open in Web Editor NEW
122.0 17.0 19.0 5.88 MB

Guard middleware for React Router navigation

Home Page: https://www.npmjs.com/package/react-router-guards

JavaScript 6.38% TypeScript 93.62%
reactjs hooks rollup typescript

react-router-guards's Introduction

Logo

react-router-guards

Guard middleware for React Router navigation

React Router Guards provides a middleware API for React Router, allowing you to perform complex logic between the call for navigation and the final render of a route.

Table of Contents

Requirements

This package has the following peer dependencies:

Installation

With npm:

$ npm install react-router-guards

With yarn:

$ yarn add react-router-guards

Then with a module bundler like webpack, use as you would anything else:

// using ES6 modules
import { GuardProvider, GuardedRoute } from 'react-router-guards';

// using CommonJS modules
const GuardProvider = require('react-router-guards').GuardProvider;
const GuardedRoute = require('react-router-guards').GuardedRoute;

Basic usage

Here is a very basic example of how to use React Router Guards.

import React from 'react';
import { BrowserRouter } from 'react-router-dom';
import { GuardProvider, GuardedRoute } from 'react-router-guards';
import { About, Home, Loading, Login, NotFound } from 'pages';
import { getIsLoggedIn } from 'utils';

const requireLogin = (to, from, next) => {
  if (to.meta.auth) {
    if (getIsLoggedIn()) {
      next();
    }
    next.redirect('/login');
  } else {
    next();
  }
};

const App = () => (
  <BrowserRouter>
    <GuardProvider guards={[requireLogin]} loading={Loading} error={NotFound}>
      <Switch>
        <GuardedRoute path="/login" exact component={Login} />
        <GuardedRoute path="/" exact component={Home} meta={{ auth: true }} />
        <GuardedRoute path="/about" exact component={About} meta={{ auth: true }} />
        <GuardedRoute path="*" component={NotFound} />
      </Switch>
    </GuardProvider>
  </BrowserRouter>
);

Check out our demos for more examples!

Concepts

With the addition of guard middleware, the navigation lifecycle has changed.

Guard functions are the middleware between navigation and rendering.

Page components are used for setting loading and error pages.

The GuardProvider component is a high-level wrapper for your entire routing solution.

The GuardedRoute component acts as a replacement for the default Route component provided by React Router, allowing for routes to use guard middleware.

Demos

We've included some demos below to help provide more context on how to use this package!

Basic

Demo + Source

The basic demo showcases some basic functionality of route guard API with an auth example.

Intermediate

Demo | Source

The intermediate demo uses the PokéAPI to showcase how to use route guards for fetching data from an API.

Contributing

We welcome all contributions to our projects! Filing bugs, feature requests, code changes, docs changes, or anything else you'd like to contribute are all more than welcome! More information about contributing can be found in the contributing guidelines.

Code of Conduct

Upstatement strives to provide a welcoming, inclusive environment for all users. To hold ourselves accountable to that mission, we have a strictly-enforced code of conduct.

About Upstatement

Upstatement is a digital transformation studio headquartered in Boston, MA that imagines and builds exceptional digital experiences. Make sure to check out our services, work, and open positions!

react-router-guards's People

Contributors

beahuang avatar dependabot[bot] avatar joshpensky avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

react-router-guards's Issues

How to access redux Store from inside the route guard

I need to access the store state and actions from inside the guard passed to the GuardProvider but I couldn't.

I've tried in multiple ways to access the store from inside the guard.

The final solution I had is making a HOC around GuardRoute that have access to the store using connect function

But the projects at the first render is being empty, and when we change the route at least one time the projects get updated.

Add to that, when I pass an action and get called inside the guard, the action is not dispatched!!

export const CustomRoute = connect(
    (state: any) => ({ state }),
    { openWarningDialog }
)
    (function _customRoute(props: RouteType) {

        return <GuardedRoute
            {...props}
            meta={{
                ...props?.meta,
                projects: props?.state?.getIn(['projects'])?.toJS(),
                openWarningDialog
            }} />
    });

"this is undefined" warning in package build

Description

There's a warning in the main package rollup build where it says that this is undefined.

Steps to reproduce

  1. Comment out the onwarn option in the rollup config
  2. Run the build command: npm run build
  3. See the console output

Expected result

There should be no warning, as the build itself does not fail.

Actual result

There is a warning, as seen below:

`this` has been rewritten to `undefined`

image

Environment

  • OS: macOS 10.13.6
  • Browser and its version: Chrome 75.0.3770.100

How to get data from useSelector

I am using Redux saga with selectors. So, first of all I use:

 useEffect(() => {
    dispatch(fetchAuthInfo());
...

to get the data, and then

 const user = useSelector(selectAuthInfo);

But in the guard it doesn't wait till the actions dispatches and it just takes default values from store

const requireLogin = (to, from, next) => {
  // console.log(user);
  if (to.meta.auth) {
    if (user.isLoggedIn) {
      next();
    } else {
      console.log('LOGIN');
      // authService.login();
      next();
    }
  } else {
    next();
  }
};

How do I make requireLogin to wait until useSelector retriggers and refreshes the store?

Support for nested route through GuardedRoute component

Summary

The GuardedRoute API should have an interface children accepting an array of GuardedRoute which will create nested Routes

Basic example

If a user wants to create nested routes like following

/dashboard
/sales
/sales/new
/sales/analytics
/sales/:company/analytics

with the current flow, they will have to create 5 GuardedRoute passing the same routes as mentioned above in path.

With the children interface, we can create an array of GuardedRoute and pass it down to the parent route e.g.

const salesRoutes = [
    <GuardedRoute path="/new" />
    <GuardedRoute path="/analytics" />
    <GuardedRoute path="/:company/analytics" />
]

<GuardedRoute path="/dashboard" />
<GuardedRoute path="/sales" children={salesRoutes} />

Motivation

While working on a project I thought this would be a great feature in terms of developer experience.

Routes do not transition when used with guardians

Summary

Goodnight. I'm using the react-router-dom package, with it I can animate the transition between my application's routes, however, when I add guardians to the routes they stop showing the transition. Does anyone know how I can get around this?

Relevant information

Provide as much useful information as you can
"react-router-dom": "^5.2.0",
"react-router-guards": "^1.0.2",
"react-transition-group": "^4.4.2",

Environment (if relevant)

Failed to Compile after version upgrade of react-router-dom from 5V to V6

Description

Failed to compile.

./node_modules/react-router-guards/dist/index.es.js
Attempted import error: '__RouterContext' is not exported from 'react-router' (imported as 's').

Steps to reproduce

Clear steps describing how to reproduce the issue.

Expected result

What should happen?

Actual result

What happened.

Environment

  • OS:
  • Browser and its version:
  • React Router DOM version:

Redirect to same path with different search querystring

Description

I am running into an issue with a guard that redirects to the same path.
What I'd like to do in the guard is to check if the route was called without a search query parameter.
If that's the case, a default search query parameter is applied and next.redirect(samePath?param=new) is invoked.

Steps to reproduce

Basic example:

const requireViewParameter = (to: GuardToRoute, _from: GuardFunctionRouteProps | null, next: Next) => {
    const query = to.location.search;
    if (query.length > 0) {
      return next();
    }

    console.log('No id was was set, redirecting to the default view');
    
    next.redirect({
      ...to.location,
      search: `?id=1`,
    });
};

<GuardProvider guards={[requireViewParameter]}>
    <GuardedRoute exact path={'view'} component={View} />
</GuardProvider>

Open the application and set the path to /view without any query parameters

Expected result

The guard runs and the URL is updated to /view?id=1

Actual result

The guard runs and invokes the redirect, but the new query parameter is not applied. The app stays on /view instead.

Environment

  • OS: Windows 10
  • Browser and its version: Chrome Canary 99.0.4819.0 (Official Build) 64-bit
  • React Router DOM version:
    • "react": "17.0.2"
    • "react-router-dom": "5.3.0"
    • "react-router-guards": "^1.0.2"

Update incorrect README usage documentation

Description

As noted in #29, we have some bad documentation in our usage README! This incorrectly shows that GuardProviders can be nested around GuardedRoutes. As of React Router v5, this is not possible, as a Switch is required in order to render only one route at a time.

We should update this documentation to correctly show an example of conditional guards using the meta prop on GuardedRoutes

The parent component gets reloaded when navigating between child/nested components

Summary

I am using nested GuardProvider one for public URLs and another one is for dashboard pages. Whenever I do navigation inside dashboard routes the parent also gets refreshed i.e. my dashboard layout with side nav also gets reloaded (not actual reload but I see the loader provided in the parent GuardProvider). I have a component DashboardLayout inside that only I am using GaurdProvider to show the content or say nested routes.

Relevant information

// public routes
<GuardProvider loader={abcLoader}>
    <GuardedRoute path="/signin" component={signin}>    </GuardedRoute>
    <GuardedRoute path="/dashboard" component={dashboard}>    </GuardedRoute>
</GuardProvider>
// dashboard component

// layout
    // header
    // side navigation
    // content
        <GuardProvider loader={xyzLoader}>
            <GuardedRoute path="/" component={home}>    </GuardedRoute>
            <GuardedRoute path="/analytics" component={analytics}>    </GuardedRoute>
        </GuardProvider>

in the above-given example when I am doing navigation from / i.e. home component to /analytics i.e. analytics component I see the abcLoader first and then the xtzLoader.

Is this known behaviour? or a bug? I wasn't sure so I put it in question.

Note

I am wrapping the parent GuardProvider inside ErrorBoundary and Suspense with the same abcLoader because I am using lazy load of components with React.lazy

multiple guardprovider support

Allow multiple guardproviders with different guards

Basic example

I want to redirect not logged in users to the login page, and if logged in people navigate to the login page, redirect them to the homepage.

      <Router history={history}>
        <NavigationBar />
        <Switch>
          <GuardProvider guards={[requireLoggedOut]}> {/* this one works */}
            <GuardedRoute path="/auth" exact component={AuthSwitchPage} />
            <GuardedRoute path="/auth/redirect" exact component={PostAuthRedirectPage} />
            <GuardedRoute path="/auth/register/pincode" component={PincodeRegisterPage} />
          </GuardProvider>
          <GuardProvider guards={[requireLogin]}>{/* this does not work anymore, no errors thrown however. */}
            <GuardedRoute
              path="/form/:formtakementId/:stepId"
              exact
              component={FormTakementStep}
            />
            <GuardedRoute
              path="/form/:formtakementId"
              exact
              component={FormLandingPage}
            />
            <GuardedRoute path="/" exact component={OverviewPage} />
          </GuardProvider>
        </Switch>
      </Router>

Can support for multiple providers be added, and is there a temporary workaround?

How to use error object in error page component with TypeScript?

In a TypeScript project, I'd like to implement an error page like the one from the error page examples:

const NotFound = ({ error }) => (
  <div>
    <h1>Not found.</h1>
    <p>{error}</p>
  </div>
);

// ...

<GuardProvider error={NotFound}>
  // ...
</GuardProvider>

But this results in a TypeScript error:

TS2322: Type '({ error }: { error: any; }) => Element' is not assignable to type 'PageComponent'.
Type '({ error }: { error: any; }) => Element' is not assignable to type 'FunctionComponent<{}>'.
Types of parameters '__0' and 'props' are incompatible.

Am I doing something wrong?

Or does the way PageComponent is defined does not allow this because ComponentType is used without a type variable?

export declare type PageComponent = ComponentType | null | undefined | string | boolean | number;

Any help is much appreciated!

Always rendering wildcard route

Summary

Router wildcard is rendering both the requested route and wildcard route. Is it me, or is this a bug?
I didn't place this as a bug, since I am following the docs and it should be fine. It isn't though.

Relevant information

When trying to set up my routes using react-router-guards, I have some struggle to show a 404-page when no route was matched.
According to the docs, the following structure should word:

<GuardProvider loading={Loading} error={NotFound}>
    <GuardedRoute path="/login" exact component={Login} />
    <GuardProvider guards={[requireLogin]}>
        <GuardedRoute path="/" exact component={Home} />
        <GuardedRoute path="/about" exact component={About} />
    </GuardProvider>
    <GuardedRoute path="*" component={NotFound} />
</GuardProvider>

My structure looks like:

<GuardProvider loading="loading..." error={FourOFourPage}>
    <GuardProvider guards={[guest]}>
        <GuardedRoute exact path="/" component={LoginPage} />
        <GuardedRoute exact path="/login" component={LoginPage} />
        <GuardedRoute exact path="/register" component={RegisterPage} />
    </GuardProvider>
    <GuardProvider guards={[auth]}>
        <GuardedRoute exact path="/me" component={MePage} />
    </GuardProvider>
    <GuardedRoute path="*" component={FourOFourPage} />
</GuardProvider>

And this renders both the valid route component AND the FourOFourPage-component. I tried using a Switch component from react-router-dom, but that doesn't work properly: it makes all routes outside the GuardProvider with the guest guard blank (instead of a preferred redirect)

The guest guard i checking if a session is active, and if true, redirect to /me.

The auth guard is checking if a session is active, and if false, redirect to /login.

Environment (if relevant)

Using:
[email protected]
[email protected]
[email protected]

Hopefully someone can help me! :)

CRA demo requires refresh after initial build

Description

The CRA demo does not render after an initial build, and requires a refresh of the browser.

Steps to reproduce

  1. Run the install commands to install dependencies (npm i && npm run bootstrap)
  2. Run the start-up command (npm run start)
  3. The basic demo should automatically open in a new tab or window at localhost:3000

Expected result

The demo should be rendered in the browser.

Actual result

The demo is not rendered in the browser. There are no console errors, but the page is blank and no DOM elements have been appended.

Environment

  • OS: macOS 10.13.6
  • Browser and its version: Chrome 75.0.3770.100
  • React Router DOM version: 5.0.1

RouteComponentProps error with react v17 and react-router-dom v5

Description

I'm using react v17 with react-router-dom v5 and I'm having a problem with imports and types, where type.d.ts sends a message saying "history is not defined" and The module' "react-router-dom" ' has no exported member 'RouteComponentProps'.ts(2305).

I saw that many are having the problem with the latest version, but I am having an error with an older version, I tried to use react v18 with RRD v5 but I had other problems.

Steps to reproduce

Follow steps described in documentation to reproduze a same reduce

  • React v17
  • React-router-dom v5

Expected result

React guards function return a component or router in page

Actual result

"type.d.ts sends a message saying "history is not defined" and The module' "react-router-dom" ' has no exported member 'RouteComponentProps'.ts(2305)."

Environment

  • OS: win 10
  • Browser and its version: [Chrome 115 on Windows 10]
  • React Router DOM version: v5

Redirect to the same route with different params

Description

If I create a guard function that redirects to the same matched route, but with different params, the guard gets stuck in an infinite redirect loop and gives up eventually with an unrelated error.

Steps to reproduce

To the intermediate sample in src/router/index.tsx I added this GuardFunction:

const invalidName = (to, from, next) => {
  const { name } = to.match.params;
  if (name === 'test') {
    next.redirect('/charmander');
  }
};

and modified the GuardedRoute:

<GuardedRoute
            path="/:name"
            exact
            component={Detail}
            guards={[invalidName, waitOneSecond, detailBeforeEnter]}
          />

Expected result

When given the route localhost:3001/test, I will get redirected to localhost:3001/charmander.

Actual result

When we get to the point of resolving the result to render react-router-dom components, matchPath returns true and the cycle continues.

if (pathToMatch && !matchPath(pathToMatch, { path, exact })) {

I'm guessing this was done to avoid infinite redirect loops, but here it's causing one.

Environment

  • OS: macOS Catalina
  • Browser and its version: Chrome 87.0.4280.141
  • React Router DOM version: 5.2.0

Specifying dist in CRA demo

Description

After switching to Lerna for managing the repo, there's now an issue with the symlink made to the CRA basic demo.

In order to import the components, we must now specify the dist folder:

import { GuardProvider, GuardedRoute } from 'react-router-guards/dist`;

This is a non-issue for the custom webpack config used in the intermediate demo.

This may be an issue with Lerna's symlink mixed with CRA's webpack config.

Redirect to requested page

Is there a way to "record" what the requested protected route was?

Say for example:

import React from 'react';
import { BrowserRouter } from 'react-router-dom';
import { GuardProvider, GuardedRoute } from 'react-router-guards';
import { About, Home, Loading, Login, NotFound } from 'pages';
import { getIsLoggedIn } from 'utils';

const requireLogin = (to, from, next) => {
  if (to.meta.auth) {
    if (getIsLoggedIn()) {
      next();
    }
    next.redirect('/login');
  } else {
    next();
  }
};

const App = () => (
  <BrowserRouter>
    <GuardProvider guards={[requireLogin]} loading={Loading} error={NotFound}>
      <Switch>
        <GuardedRoute path="/login" exact component={Login} />
        <GuardedRoute path="/" exact component={Home} meta={{ auth: true }} />
        <GuardedRoute path="/about" exact component={About} meta={{ auth: true }} />
        <GuardedRoute path="*" component={NotFound} />
      </Switch>
    </GuardProvider>
  </BrowserRouter>
);

Let's say user requests "/about" and gets redirected to "/login" because they are not logged in. After logging the user in is there a way to retrieve the path the user requested so that the application can then redirect them there (in this case "/about")?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.