Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Export `react-router` Redirect from shared component library

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?

like image 243
Corey Avatar asked Jun 21 '19 17:06

Corey


People also ask

How to redirect/blog to/tutorials using React-router-Dom?

Suppose we have a path /blog in our app and we updated it to /tutorials so that now if any user tries to navigate to /blog we need to redirect them to /tutorials, we can do it by using a Redirect component provided by the react-router-dom library. In the below code, we first imported the Redirect component from the react-router-dom library.

Why can't I redirect on initial render in react?

We no longer support redirecting on the initial render, due to compatibility issues with future versions of React React won't let us change the state in an ancestor component on the initial render w/out warning, so we had to remove the component, as well as the ability to do a navigate () on the initial render.

What is the difference between redirect and navigate component?

The Navigate component changes the current location when it's rendered. Copied! In version 6 of react router, the Redirect component is replaced with Navigate. When the Navigate component is rendered, it changes the current location.

How to get the history of an object in react-router?

First way using the Redirect component. Second way using the history object which is available inside props, passed by the react-router library.


2 Answers

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
like image 162
Corey Avatar answered Oct 09 '22 00:10

Corey


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

like image 3
Mosè Raguzzini Avatar answered Oct 08 '22 23:10

Mosè Raguzzini