Varvara Stepanova
Varvara Stepanova

Reputation: 3569

How to avoid `loaded two copies of React` error when developing an external component?

I am developing an external component (let's say my-component, which I link to the project with npm link (as it is in process and I need the package to reflect changes).

In the my-component folder there are node_modules/react and node_modules/react-dom as they are its dependencies. However they are peerDependences, so I did not suppose to bring them into the project linking this package.

However when using npm link, it link the whole directory, including node_modules. So, when the project builds, it includes packages 2 times: from node_modules/* and from node_modules/my-component/node_modules/*.

This begins to affect when the component is using ReactDOM.findDOMNode, it causes this error:

Warning: React can't find the root component node for data-reactid value `.0.2.0`. If you're seeing this message, it probably means that you've loaded two copies of React on the page. At this time, only a single copy of React can be loaded at a time.

Also, it may help to understand what's happening: the problem only appears if there are both node_modules/my-component/node_modules/react and node_modules/my-component/node_modules/react-dom. If there is only one of them, there is no error message.

The usual package installation does not bring such error as there is no node_modules/react-dom there.

How is it supposed to develop an external component and the project at the same time?

Upvotes: 51

Views: 26375

Answers (10)

Wes
Wes

Reputation: 361

Strongly recommend using https://github.com/mweststrate/relative-deps.

Installs dependencies from a local checkout, and keeps them in sync, without the limitations of link.

This fixes the issue as it installs the local library just as npm install would, satisfying any dependency versions, etc.

Upvotes: 2

abhisekpaul
abhisekpaul

Reputation: 515

The issue is twofold:

  1. You cannot have 2 copies of react loaded.
  2. npm link creates a symlink, however the "require" doesnt respect the symlink and when it tries to navigate up the directory, it never finds the react of the parent project.

Solution:

All you have to do is link the react and react-dom in your component to that of parent project's node_modules folder.

Go to your component project and remove the react and react-dom then do

npm link ../myproject/node_modules/react
npm link ../myproject/node_modules/react-dom

Upvotes: 21

Marcus Junius Brutus
Marcus Junius Brutus

Reputation: 27286

Adding the following in my webpack.config.js worked for me:

resolve: {
    alias: {
        react: path.resolve(__dirname, 'node_modules', 'react')
    }
}

I also experimented with DedupePlugin (mentioned as a possible remedy here) but I couldn't get it to work.

What's also interesting is that I've encountered different (and perhaps more insidious) manifestations of the same problem when a module is found in multiple places in the dependency graph.

One such case was that my React.PropTypes.instanceOf(SomeType) constraints would emit warnings even though the type I passed in was correct. That was due to the module being present in multiple places in the node_modules directory tree. Due to duck-typing the code would still work, but my console was cluttered with these warnings. Going the resolve.alias way silenced those too.

YMMV

Upvotes: 2

Yuri
Yuri

Reputation: 863

Fixed it by adding react-dom as an alias to my webpack config

alias: {

    react$: require.resolve(path.join(constants.NODE_MODULES_DIR, 'react')),
    'react-dom': require.resolve(path.join(constants.NODE_MODULES_DIR, 'react-dom'))

}

Upvotes: 8

Ben
Ben

Reputation: 57247

I was getting this because I had already included react and react-dom as external scripts in my HTML markup.

The error was caused by adding an import ReactDOM from 'react-dom' to a component module. The error went away once I removed the import, and the module worked fine since the components were already available.

Upvotes: 0

ericsoco
ericsoco

Reputation: 26253

Someone clevererer than I (@mojodna) came up with this solution: remove the duplicate dependencies from the external component, and resolve them with your project's copies of those deps.

Step 1: Remove the dependencies from your external component's node_modules

As @cleong noted, you can't just remove the deps from the external component's node_modules, because your project's build step will fail when it hits the now-missing dependencies in the external component.

Step 2: Add your project's node_modules to NODE_PATH

To fix this, you can append the project's node_modules to the NODE_PATH environment variable when running your build step. Something like e.g. this:

NODE_PATH=$(pwd)/node_modules/ npm start

(where npm start is your script that bundles your external component, via e.g. Browserify, Webpack, etc.)

In fact, you could always append that NODE_PATH addition to your build scripts, and it would work whether or not you've npm linked anything. (Makes me wonder if it shouldn't be default npm behavior...)

Note: I left my existing answer because there's some conversation there, and this is a different (and better) solution.

Upvotes: 6

user1641172
user1641172

Reputation:

I am using ReactJS.net and setup webpack from the tutorial there and started using react-bootstrap aswell when i started getting this error. I found adding 'react-dom': 'ReactDOM' to the list of externals in webpack.config.js fixed the problem, the list of externals then looked like this:

  externals: {
    // Use external version of React (from CDN for client-side, or
    // bundled with ReactJS.NET for server-side)
      react: 'React',
      'react-dom': 'ReactDOM'

This seems to be the first stack overflow link on google for this error, so i thought this answer might help someone here.

Upvotes: 1

Thomas Kagan
Thomas Kagan

Reputation: 438

The problem is with npm link. https://github.com/npm/npm/issues/5875

npm doesn't treat the linked directory as a child of the parent project.

Try alternatives to npm link:

1) Use relative path dependencies in package.json

2) Manually include your dependencies in your projects node_modules directory

3) Use url path

Basically anything but npm link

Upvotes: 3

ncherro
ncherro

Reputation: 2614

If you're using Webpack in the main project, this solution may work. In my case, project-a requires project-b. Both require React and ReactDOM 0.14.x

I have this in project-a/webpack.config.js:

resolve: {
  modulesDirectories: ['node_modules'],
  fallback: path.join(__dirname, 'node_modules')
},
resolveLoader: {
  fallback: path.join(__dirname, 'node_modules')
},
  • project-b requires React and ReactDOM as peerDependencies in project-b/package.json
  • project-a requires project-b as a devDependency (should also work required as a dependency) in project-a/package.json
  • local project-b is linked to project-a like so: cd project-a; npm link ../project-b

Now when I run npm run build within project-b, the changes appear immediately in project-a

Upvotes: 0

ericsoco
ericsoco

Reputation: 26253

I believe the answer is to specify react and react-dom as peerDependencies in your external component's package.json. As best as I can follow here and here, npm link should (as of npm@3) no longer install peerDependencies (or `devDependencies either).

Aaaand I just read your post more carefully and realized that you already are specifying them as peerDependencies. Therefore, I think the answer boils down to:

Upgrade to npm@3:

npm install -g [email protected]

Upvotes: 4

Related Questions