React components library rendering React Links

Viewed 1133

I developed a React components library that I'm using from some of my projects.

One of them is a Storybook, so I created a BrowserRouter decorator to add to my stories and the components render perfectly just for showing and playing around purposes.

However, when using some of the components from another project, which has a BrowserRouter on the top level of my app components tree, I get this error:

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

Here, you can see a brief snippet of what I'm trying to render from my ConsumerProject:

index.js:


import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter } from 'react-router-dom';
import { LibraryComponent } from 'my-library';

const element = (
      <HotEnabler>
        <BrowserRouter>
          <RouterTrigger trigger={pathname => triggerHooks(_routes, pathname)}>
            <ApolloProvider client={client}>
              <ApolloProviderHooks client={client}>
                <LibraryComponent />
              </ApolloProviderHooks>
            </ApolloProvider>
          </RouterTrigger>
        </BrowserRouter>
      </HotEnabler>
);

ReactDOM.render(element, destinationDomElement);

Here, you can see the most important parts of MyLibrary:

package.json:

...
"peerDependencies": {
    "react": ">=16.8.6",
    "react-dom": ">=16.8.6",
    "react-router": ">=5.0.0",
    "react-router-dom": ">=5.0.0",
    "styled-components": ">=4.1.0"
}

libraryComponent.js:

import React from 'react';
import PropTypes from 'prop-types';
import { Link } from 'react-router-dom';

import { SCLink } from './styles';

const LibraryComponent = props => {
  return (
      <Link
        to={'/any-route/'}
      >
        Testing Links
      </Link>
    );
  }
};

export default LibraryComponent;

I wanted to add something important:

If I add this alias to the Webpack config of ConsumerProject:

react: path.join(constants.ROOT_DIR, '../my-library/node_modules/react/'),
'react-dom': path.join(constants.ROOT_DIR, '../my-library/node_modules/react-dom/'),
'react-router-dom': path.join(constants.ROOT_DIR, 'node_modules/react-router-dom/')

Everything works fine. If I don't add them, I get errors both from React and from React Router Dom.

1 Answers

I had the same problem with my library that had components depending on react-route-dom. After some investigation, I came to the conclusion that in my project app, there are 2 versions of react-router-dom bundled (even the same version), which creates 2 Context instances that conflict with each other.

The solution in my case was to move react-router-dom to peerDependencies, and exclude it from library build with webpack configuration:

externals: {
  'react-router-dom': {
    root: 'ReactRouterDom',
    commonjs2: 'react-router-dom',
    commonjs: 'react-router-dom',
    amd: 'react-router-dom',
  },
},

More in-depth information was found here.

Related