Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Solve having more than one copy of React in the same app

I'm developing a React module locally. For that, I'm linking my module using npm link. The module is imported successfully but hooks are failing inside the module. It's throwing the following error:

Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons: 1. You might have mismatching versions of React and the renderer (such as React DOM) 2. You might be breaking the Rules of Hooks 3. You might have more than one copy of React in the same app See https://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem.

Checking the suggestions at React docs, I can confirm my app is using duplicate versions of React since the following code returns false:

// node_modules/mymodule/src/index.js
export { default as ReactFromModule } from 'react'
// src/index.js
import React from 'react'
import { ReactFromModule } from 'mymodule'
console.log(React === ReactFromModule) //false

This issue is full of suggestions but they are confusing. How can I solve it?

Note: Im not breaking rules of hooks, the error appears only when importing the module from an application.

like image 745
Rashomon Avatar asked Mar 05 '21 07:03

Rashomon


People also ask

How do you solve You might have more than one copy of React in the same app?

4 answers. In the module you are developing, add the conflicting packages to peerDependencies (and remove them from dependencies or devDependencies ): // package. json "peerDependencies": { "react": "16.13.

Can a React app have more than one page?

The use cases for having multiple pages in a single React app are pretty simple. You can create a website, and easily classify different types of content on different pages. But, it should also be understood that the default implementation of React is made to use a single HTML file, and this is by design.

How can you embed two or more React components into one?

We can divide the UI of the application into small components and render every component individually on the web page. React allows us to render one component inside another component. It means, we can create the parent-child relationship between the 2 or more components.

How do I merge two React apps?

If you just want the two apps to merge fully together, I would recommend merging the src folders together (possibly manually) along with any dependencies (i.e. combining the package. json files) and then re-running npm install.

How to make all react modules refer to the same React library?

If you use webpack to build your application, you can use the resolve.alias configuration option to make sure that all modules refer to the same react library: If you use web pack then you can fix it by adding the following to Webpack config and if you don't use webpack?

Why can't I add a ref to a React component?

Only a ReactOwner can have refs. You might be adding a ref to a component that was not created inside a component's render method, or you have multiple copies of React loaded I have tried many solutions from diff forum but none of them worked.I am sure problem is of multiple copies of react.

Does moving react to peerdependencies resolve the issue?

In response to another comment, merely moving React to peerDependencies does not adequately resolve the issue in all cases. I would reply to that comment directly, but StackOverflow requires more reputation to respond to wrong answers than it does to post them.

How do I add react to a Webpack module?

In the module you are developing, add the conflicting packages to peerDependencies (and remove them from dependencies or devDependencies ): Execute npm install in your module. Now add react and react-dom to the webpack configuration of your module as externals.


4 Answers

In the module you are developing, add the conflicting packages to peerDependencies (and remove them from dependencies or devDependencies):

  // package.json
  "peerDependencies": {
    "react": "16.13.1",
    "react-dom": "16.13.1"
  },

Execute npm install in your module.

Now add react and react-dom to the webpack configuration of your module as externals. These packages shouldnt be included in the bundle of the module (the app that uses the module will provide them):

// webpack.config.js
module.exports = {
    /*
    rest of config...
    */
    output: {
        filename: "index.js",
        pathinfo: false,
        libraryTarget: 'umd', // In my case, I use libraryTarget as 'umd'. Not sure if relevant
    },
    externals: {
        // Use external version of React
        "react": {
            "commonjs": "react",
            "commonjs2": "react",
            "amd": "react",
            "root": "React"
        },
        "react-dom": {
            "commonjs": "react-dom",
            "commonjs2": "react-dom",
            "amd": "react-dom",
            "root": "ReactDOM"
        }
    },
};

Then, after building your module, in your application you can check that both versions are now the same:

// node_modules/mymodule/src/index.js
export { default as ReactFromModule } from 'react'
// src/index.js
import React from 'react'
import { ReactFromModule } from 'mymodule'
console.log(React === ReactFromModule) // true :)
like image 53
Rashomon Avatar answered Oct 21 '22 15:10

Rashomon


In response to another comment, merely moving React to peerDependencies does not adequately resolve the issue in all cases. I would reply to that comment directly, but StackOverflow requires more reputation to respond to wrong answers than it does to post them.

I have a shared React component module built using Webpack and have run into the same issue. I've outlined one possible fix in this comment below which requires modifying peerDependencies and using npm link in a fashion similar to the answer shared by mtkopone. https://github.com/facebook/react/issues/13991#issuecomment-841509933

My solution is a bit hacky and I wouldn't recommend it for long-term use. If you are using Webpack (which you tagged this question as), this article may detail a more permanent solution (https://medium.com/codex/duplicate-copy-of-react-errors-when-using-npm-link-e5011de0995d). I haven't tried it yet, but the author seems to have tried all the (incorrect) solutions out there and is also running into the hooks issue while trying to build shared component libraries.

The author of that article is trying to debug a Create-React-App app. While CRA uses webpack under the hood, you can't access the webpack.config directly, so the author has to perform some workarounds to do so. If you aren't using CRA, but just plain Webpack, then you could consider using the resolve.alias section of webpack.config to ensure there are no duplicate copies of React (see: https://blog.maximeheckel.com/posts/duplicate-dependencies-npm-link/)

like image 43
Vincent La Avatar answered Oct 21 '22 15:10

Vincent La


Adding react and react-dom as peerDependencies in the package.json didn't work for me.

I had to add an alias to the webpack configuration file:

// webpack.config.js
resolve: {
  alias: {
    react: path.resolve('./node_modules/react'),
}
like image 35
L. Pier Roberto Avatar answered Oct 21 '22 16:10

L. Pier Roberto


I was attempting to use the peerDependencies and removal of the devDependencies and it was failing.

It turned out I had a node_modules folder in one of the parent folders of the library I was working on and the duplicate version of React was being loaded from there instead of the tool that was trying to use the React library.

Rather than editing the devDependencies to remove react I just wrote a small script to delete anything that's in the peerDependencies from the node_modules folder.

npm view --json=true . peerDependencies | jq -r 'keys | .[] |  @text' | while read dep; do  rm -r ./node_modules/${dep} && echo Removed ${dep}; done
like image 36
Matthew Buckett Avatar answered Oct 21 '22 14:10

Matthew Buckett