12

I have a shared (React) component library that I'm building. There is a PrivateRoute component that I am wanting to include. However, when I import the component from the module library into another application, I get an error:

Error: Invariant failed: You should not use <Redirect> outside a <Router>

The PrivateRoute component wraps the react-router/Route component with authentication logic and redirects unauthenticated requests to login:

component-library

import { Route, Redirect } from 'react-router';
/* ... */

class PrivateRoute extends Component {
  /* ... */
  render() {
    const {
      component: Comp, authState, loginPath, ...rest
    } = this.props;

    return (
      <Route
        {...rest}
        render={props => authState === SIGNED_IN ? (
          <Comp {...props} />
        ) : (
          <Redirect
            to={{
              pathname: loginPath,
            }}
          />
        )}
      />
    );
  }
}

I then import the component into a separate (React) project:

create-react-app

import { Router } from 'react-router';
import { PrivateRoute } from 'component-library';
/* ... */

class App extends Component {
  // "history" is passed in via props from the micro frontend controller.
  /* ... */

  render() {
    return (
      <Router history={this.props.history}>
        {/* ... */}
        <PrivateRoute path="/protected" component={ProtectedView} />
      </Router>
    );
  }
}

This will work as expected if the PrivateRoute component is defined in the create-react-app application. However, moving this component to the shared library results in the error.

I have tried building the library with webpack output libraryTarget set to commonjs2. But, I've also tried umd. I've also tried with Rollup. All with the same results.

webpack.config.js

module.exports = {
  //...
  output: {
    path: path.resolve(__dirname, 'dist/'),
    publicPath: '',
    filename: '[name].js',
    libraryTarget: 'commonjs2',
  },
  //...
};

My assumption is that the issue is with building the component library as the Invariant error is thrown when Redirect is unable to find the RouterContext. Although the library builds without errors, it seems that importing compiled/built code is a problem.

Could also be two instances of React causing an issue with the Context API. However, react-router is not using the Context API. It's using the mini-create-react-context polyfill.

Any thoughts or ideas on how to resolve this?

Corey
  • 5,818
  • 2
  • 24
  • 37
  • Where you are importing Router from ? – Mosè Raguzzini Jun 24 '19 at 14:42
  • Importing Router from 'react-router'. 'react-router' is a production dependency in the component library. So, the 'create-react-app' does not list 'react-router' as a dependency, but imports the Router from 'react-router' and PrivateRoute from the component library. – Corey Jun 24 '19 at 14:58
  • Is there a way to get a sandbox that reproduces this? I've tried at https://codesandbox.io/s/a-simple-react-router-v4tutorial-stnxs but I can't manage to – apokryfos Jun 24 '19 at 16:22

4 Answers4

3

You have to import router (assuming you're using V4) from react-router-dom, eg:

import { BrowserRouter as Router, Route, Link } from "react-router-dom";

In v4, react-router exports the core components and functions. react-router-dom exports DOM-aware components, like <Link> (which renders an <a>) and <BrowserRouter> (which interacts with the browser's window.history ).

react-router-dom re-exports all of react-router's exports, so you only need to import from react-router-dom in your project.

Ref: https://github.com/ReactTraining/react-router/issues/4648#issuecomment-284479720

Corey
  • 5,818
  • 2
  • 24
  • 37
Mosè Raguzzini
  • 15,399
  • 1
  • 31
  • 43
  • 1
    Thank you for the response. I'm using `Router` because I have a unique situation where I have to define the history with `createBrowserHistory` (much like `BrowserRouter`). We have a micro frontend architecture and the history is shared between the registered applications to maintain state. – Corey Jun 24 '19 at 15:36
  • Yes but to manage router render you need anyway react-router-dom, mate – Mosè Raguzzini Jun 24 '19 at 15:54
1

I did finally discover the issue which had little to do with react-router and more with React. I found that this error would only show in local development because the component-library was linked via npm link.

The resolution came from this answer: https://stackoverflow.com/a/38818358/715597

The solution in my case was to link React and React Router in the component library to the applications reference of React and React Router:

# link the component library
cd my-app
npm link ../component-library

# link its copy of React back to the app's React
cd ../component-library
npm link ../my-app/node_modules/react
npm link ../my-app/node_modules/react-router
Corey
  • 5,818
  • 2
  • 24
  • 37
0

Can't you do it using a function

if(authState === SIGNED_IN){
   return <Route
        {...rest}
        render={<Comp {...props} />
        />
}else{
   // here you can use window.location 
   // or 
   // render a different component (which shows unauthorized or something you want)
}
Tenusha Guruge
  • 2,147
  • 3
  • 18
  • 38
-1

you only needed to remove dependency "react-router-dom": "5.0.0" from package.json to fix the error.

Manrah
  • 556
  • 3
  • 13