5

I have a monorepo with multiple apps & libraries in it which works really well (yarn based). I did some refactoring lately and identified a common component which I wanted to "outsource" in my 'commons' library. This contains a component that uses a ReactRouter.Route:

import React from 'react';
import { Route, useHistory } from 'react-router-dom';

export const AuthenticatedRoute = (props: Props): JSX.Element => {
   const history = useHistory(); // Crashes here

   ... do auth stuff ...

   useEffect(() => {
     if (!authenticated) {
        history.push(...);
     }
   }, []);

   return (
     <Route {...props}>
       {props.children}
     </Route>
   );
}

This component works like a charm when contained my react app. However, when extracted into the commons library, it fails at runtime with the following error:

Uncaught TypeError: Cannot read property 'history' of undefined
    at useHistory (index.js:5914)
    at AuthenticatedRoute (index.js:9848)
    at renderWithHooks (react-dom.development.js:16260)
    at mountIndeterminateComponent (react-dom.development.js:18794)
    at beginWork$1 (react-dom.development.js:20162)
    at HTMLUnknownElement.callCallback (react-dom.development.js:336)
    at Object.invokeGuardedCallbackDev (react-dom.development.js:385)
    at invokeGuardedCallback (react-dom.development.js:440)
    at beginWork$$1 (react-dom.development.js:25780)
    at performUnitOfWork (react-dom.development.js:24695)

I guess this is somehow related on how the library / app reference the react-router-dom library.

commons/package.json:

{
  "name": "commons",
  "version": "1.0.0",
  "main": "dist/index.js",
  "private": true,
  "dependencies": {
  },
  "peerDependencies": {
    "react-router-dom": "^5.1.2"
  }
}

app/package.json:

{
  "name": "app",
  "version": "1.0.0",
  "private": true,
  "dependencies": {
    "react": "^16.12.0",
    "react-dom": "^16.12.0",
    "react-router-dom": "^5.1.2",
    "commons": "1.0.0"
  }
}

app/app.ts:

import React from 'react';
import { BrowserRouter as Router } from 'react-router-dom';

export const App = (): JSX.Element => {
  return (
    <Router>
      <AuthenticatedRoute />
    </Router>
  );
};

Note: If I comment the useHistory, it fails at the <Route> saying it needs to be a child of a <Router> which it is.

Things I tried:

  • use peerDependencies so that the library & app use the same instance of react router
  • clean & yarn install multiple times
  • wild combinations of react-router & react-router-dom dependencies
HoldOffHunger
  • 18,769
  • 10
  • 104
  • 133
Mike Kasperlik
  • 311
  • 2
  • 12
  • Are you 100% sure it's wrapped in a router? do other hooks like `useParams` work? – azium Apr 16 '20 at 13:49
  • Yes. When changing nothing but coping the `AuthenticatedRoute` (and updating the imports ofc) into the `app` module it works. Other hooks, such as `useEffect` work fine, no warnings in console either. – Mike Kasperlik Apr 16 '20 at 14:03
  • 1
    @MikeKasperlik were you able to find a solution for this problem? I have the same issue – Borys Kupar Nov 03 '20 at 17:00
  • I did not found a solution - we ended up including the files directly in the application. – Mike Kasperlik Nov 20 '20 at 14:11
  • 2
    I had exactly this problem in a monorepo and this post had the solution for me: https://stackoverflow.com/questions/56750633/react-components-library-rendering-react-links Bascially move react-router-dom to peerDepencencies in the library and exclude it from the build in the rollup script. – Dean Oct 02 '21 at 04:02

1 Answers1

-2

withRouter is a HOC. you must use it accordingly. from documentation:

import { withRouter } from "react-router";

class ShowTheLocation extends React.Component {
  static propTypes = {
    match: PropTypes.object.isRequired,
    location: PropTypes.object.isRequired,
    history: PropTypes.object.isRequired
  };

  ...
}

const ShowTheLocationWithRouter = withRouter(ShowTheLocation);

Initialization of BrowserRouter seems fine.

bbortt
  • 405
  • 2
  • 13
  • nah that's old and doesn't solve the problem op is having – azium Apr 16 '20 at 13:45
  • depending on the version. what I intended to say was that `useHistory` might not exist and therefore he cannot read something from `undefined`. he might got caught with an outdated example. > "There is no useHistory hook in 6.0. Switch back to 5.1.2 or adjust your code to not use that hook" from https://github.com/ReactTraining/react-router/issues/7189. although range seems ok on first sight. – bbortt Apr 16 '20 at 13:58
  • Yeah this is pre-hooks. I also use 5.1.2 - see the initial post dependencies. As this component works when copied 1:1 into `app` this can't be in the code itself but has to be something with dependencies. – Mike Kasperlik Apr 16 '20 at 14:01
  • did you try creating a history explicitly and using `Router` from `react-router`? – bbortt Apr 16 '20 at 14:07