Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Hooks + multiple instances of React #13991

Open
brunolemos opened this issue Oct 27, 2018 · 86 comments

Comments

Projects
None yet
@brunolemos
Copy link

commented Oct 27, 2018

To people coming from search: please read this page first. It contains most common possible fixes!

Do you want to request a feature or report a bug?

Enhancement

What is the current behavior?

I had multiple instances of React by mistake.

When trying to use hooks, got this error:
hooks can only be called inside the body of a function component

Which is not correct since I was using function components. Took me a while to find the real cause of the issue.

What is the expected behavior?

Show the correct error message. Maybe detect that the app has multiple instances of React and say that it may be the reason of bugs.

@brunolemos brunolemos referenced this issue Oct 27, 2018

Closed

🎣 Using React Hooks in React Native #21967

3 of 3 tasks complete

@brunolemos brunolemos changed the title Wrong hooks error message when app has multiple instances of React Hooks + multiple instances of React Oct 27, 2018

@philipp-spiess

This comment has been minimized.

Copy link
Collaborator

commented Oct 27, 2018

So just for clarification: You were importing a hook (say useState) from a different react module than the one used to render the component?

I agree that this is confusing. I'm not sure though if we have a way of knowing if any other React module is rendering. AFAIK we try to run React in isolation as much as possible so that multiple React instances can work in the same global context without issues.

Otherwise we could probably update the error message and mention this case as well if it's not too confusing.

@brunolemos

This comment has been minimized.

Copy link
Author

commented Oct 27, 2018

Yes, I compared React1 === React2 and it was false (React1 being from index.js and React2 being from the file using the hook). When this happens, hooks fail with the generic error message above.

This issue is to raise awareness of this case and maybe improve the error message in some way to help people that face this. It's probably very edge though.

@GabrielBB

This comment has been minimized.

Copy link

commented Nov 1, 2018

Yup, i tried to npm link a package i'm creating. It throws that same error since the other package is also using hooks but with its own React. I had to publish my package to NPM and then import it directly from NPM. That way the error was gone, but i hope this is fixed since publishing a package without testing it is bad, obviously

@mpeyper

This comment has been minimized.

Copy link

commented Nov 2, 2018

Lerna monorepos suffer from this as well when a custom hook is defined in one package and used by another as the symlinked dependencies use their own copy of react.

I have a (hacky) workaround at the moment using npm-link-shared and a prestart npm script to essentially replace the one package's react dependency with a symlink to the other's, so they use the same instance.

"prestart": "npm-link-shared ./node_modules/<other package>/node_modules . react"
@apieceofbart

This comment has been minimized.

Copy link

commented Nov 3, 2018

I had the same issue and I resolved it by adding:

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

to resolve property in webpack config of my main app.

It's was obviously my mistake of using two copies of React but I agree that it would be great if the error message was better. I think this is maybe similar to: #2402

@GabrielBB

This comment has been minimized.

Copy link

commented Nov 5, 2018

@mpeyper It works. Thanks

@jimbo

This comment has been minimized.

Copy link

commented Nov 18, 2018

@apieceofbart That worked for me. Thanks for the suggestion. 👍

@jbandi

This comment has been minimized.

Copy link

commented Dec 3, 2018

As I understand this problem arises when there are multiple copies of React in the same bundle.

Is this also a problem if two separate bundles with their own copies of React are bootstrapping their own React applications on separate dom elements, like described here: https://medium.jonasbandi.net/hosting-multiple-react-applications-on-the-same-document-c887df1a1fcd

I think the latter is a common "integration" pattern used for instance in the single-spa meta-framework (https://github.com/CanopyTax/single-spa).

@vpicone

This comment has been minimized.

Copy link

commented Dec 10, 2018

I'm also having this issue even with the exact same react versions, developing hooks to be published on their own is broken when using npm-link. Getting the same unhelpful hooks can only be called inside the body of a function component message. @apieceofbart's alias solution solved this for me. Thanks so much!

@dotlouis

This comment has been minimized.

Copy link

commented Dec 11, 2018

Same issue here when I npm link a package to my main application. I could not get babel-plugin-module-resolver working.
It says:
Could not find module './node_module/react'
This is annoying because it prevents me from testing my component locally before publishing it.

@doasync

This comment has been minimized.

Copy link

commented Dec 22, 2018

I fixed my issue by removing the caret in "react": "^16.7.0-alpha.2"
Here is the full comment: #14454 (comment)

@pelotom

This comment has been minimized.

Copy link

commented Dec 22, 2018

I'm using Yarn, and fixed this by forcing resolution in my package.json:

  "resolutions": {
    "**/react": "16.7.0-alpha.2",
    "**/react-dom": "16.7.0-alpha.2"
  },
@leecade

This comment has been minimized.

Copy link

commented Dec 24, 2018

Same here!!

@zbuttram

This comment has been minimized.

Copy link

commented Feb 5, 2019

Just wanted to leave a note here for anyone who might have had this problem in the same manner I did.

We're running React and Rails with the react-rails gem and rendering components directly into Rails views. I was receiving this error every time a new version of the app was pushed, because Turbolinks was grabbing the new JS bundle out of the <head> which loaded up an extra instance of React. Solution was to have Turbolinks do a full page reload when it detects the bundle has changed: https://github.com/turbolinks/turbolinks#reloading-when-assets-change

This appears to have solved it for us.

@taylorham

This comment has been minimized.

Copy link

commented Feb 6, 2019

I'm very excited to finally put Hooks into production, and we all owe a huge thank you to everyone who made it possible. They're a ton of fun to work with and have made my code shorter and more declarative.

Just as a heads up, this issue is still relevant in the released version with the same unhelpful error message of "Hooks can only be called inside the body of a function component."

Is this something that can be fixed? I imagine it might become more and more prevalent as more devs start to implement the new features, and a clearer error message would go a long way in lieu of an outright "fix".

Thanks again for all the hard work and congrats on the release! It's really an amazing set of features.

Edit: Should have looked closer at the open PRs, just found #14690 that addresses this. Thanks @threepointone!

@desmondgong

This comment has been minimized.

Copy link

commented Apr 30, 2019

I tried all the solutions above, but still got the error.
Eventually, I found it was caused by the package why-did-you-update, and there is a related issue for it. Just a clue for anyone uses a similar package which modifies React.

@sartaj

This comment has been minimized.

Copy link

commented Apr 30, 2019

I was able to fix this in a react-native + Yarn Workspaces scenario.


In root package.json

{
  "private": true,
  "workspaces": {
    "packages": [
      "packages/*"
    ],
    "nohoist": [
      "**/react-native",
      "**/react-native/!(react)/**"
    ]
  }
}

This prevents react-native code from being hoisted (as is needed for react native), while still hoisting react, thus having all shared modules use the same react.


In the metro.config.js

module.exports = {
  watchFolders: [
    path.resolve(__dirname, '../', 'shared-module'), 
    // ...more modules
    path.resolve(__dirname, '../', '../') // to make sure the root `react` is also part of the haste module map
  ]
}

The metro config lets the bundler know where everything is.

@Danilo-Zekovic

This comment has been minimized.

Copy link

commented May 2, 2019

I found a way to solve the problem for people who are locally developing a npm package for example, and they are trying to test it locally by loading their package using npm link in some sort of example app.

I switched from example app (original way of testing the component) to Storybook. When using the storybook in the project it will not load React twice as it will use the same one that component is using. While using npm link I had problems with hooks and React being loaded twice, also I was not able to get any of the above solutions to work. So Storybook solved my problem, and now I have a way to test my component through multiple scenarios and at the same time build some interactive documentation for it.

@elpddev

This comment has been minimized.

Copy link

commented May 9, 2019

Sharing my experience with this, solved it for our project by using webpack externals for the component library to exclude react and react-dom from the build.

We are just starting with React. In my case, we are starting with Lerna monorepo with Neutrino components library package and Neutrino webapp client package. The webapp consume the build product of the linked components library. After some experimenting and getting true for the same React instance, I have looked to a way to exclude react and react-dom from the build of the components library at all.

It seems a common design pattern solution for webpack components libraries so in the components library I have added to the webpack config:

"externals": {
  "react": "react",
  "react-dom": "react-dom"
}

I didn't need to put a global script tag in the webapp package for react and react-dom. I am guessing Webpack is providing those to the component library in its require implementation the same as it provides to the webapp.

@samuelcolvin

This comment has been minimized.

Copy link

commented May 11, 2019

another cause of this error is mis-configuring React Router's Route,

This fails:

<Route render={MyHookedComponent}/>

, but this succeeds:

<Route component={MyHookedComponent}/>

Eg. you need to use component not render. This is an easy mistake to make since render generally works fine with class based components.

@hnagarkoti

This comment has been minimized.

Copy link

commented May 13, 2019

I was working on biolerplate and want to publish it to npm, and developing with the help of npm link It was working properly but after sometime started giving errors Invalid Hook call warning.
I tried using npm link ../myapp/node_modules/react but it doesn't solve out my problem,
And compared React1 === React2 it is true as well as npm ls react also done, it shows only one package.

And i am not using webpack also, i am just adding some layer outside create-react-app so i can't forced my app to use local installed react module.
Stucked with it from past 3 days._

@rg4real

This comment has been minimized.

Copy link

commented May 15, 2019

Same as @hnagarkoti .

jaketrent added a commit to pluralsight/design-system that referenced this issue May 17, 2019

fix(site): support position component with hooks
Get the same React version as the other position component (with the
hook) is using.  What a pain.

see: facebook/react#13991 (comment)
@richardscarrott

This comment has been minimized.

Copy link
Contributor

commented May 18, 2019

I experienced this warning during server side rendering (SSR) because I was using an older version of react-apollo so just wanted to leave this link here to help out the next poor soul who runs into this issue:

apollographql/react-apollo#2541

In short, getDataFromTree doesn't support react hooks until version react-apollo@2.3.1.

@dwjohnston

This comment has been minimized.

Copy link

commented May 28, 2019

I had the same issue and I resolved it by adding:

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

to resolve property in webpack config of my main app.

It's was obviously my mistake of using two copies of React but I agree that it would be great if the error message was better. I think this is maybe similar to: #2402

Any suggestions for doing this with create-react-app?

@dwjohnston

This comment has been minimized.

Copy link

commented May 28, 2019

^ Ok, the solution I went for to solve this for create-react-app is to use react-app-rewired and customize-cra.

Here is my config-overrides.js :

const {
    override,
    addWebpackAlias,
  } = require("customize-cra");

const path = require('path'); 

module.exports = override( 
    addWebpackAlias({
        react: path.resolve('./node_modules/react')
    })
)

Example project: https://github.com/dwjohnston/material-ui-hooks-issue/tree/master

@otakustay

This comment has been minimized.

Copy link

commented May 28, 2019

In our team we have a universal navigation component work across tens of apps, all these apps comes from react 15.0.0 to react 16.8.0, in order to enable navigation implemented above hooks, we have to bundle it with a latest react

In this case, having multiple instances of react is a fundamental requirement for us, I'd like to know whether react official team is willing to solve this issue in the future?

@ricokahler

This comment has been minimized.

Copy link

commented May 28, 2019

@dwjohnston my workaround for create-react-app was to create a webpack config for development. create-react-app internally uses webpack, webpack-dev-server, and the babel-loader so creating a webpack config just for development wasn't too bad because the dependencies are already implictly there but still a good amount of overhead to get the working correctly.

I have a issue on create-react-app: facebook/create-react-app#6953 to add webpack alias support or similar.

👋 If anyone is also using create-react-app and experiencing this pain point, could you please give that issue a thumb ups? It might help prioritize the issue.

@dwjohnston

This comment has been minimized.

Copy link

commented May 28, 2019

@ricokahler - Thanks for point that out. I'm glad to see that I'm not the only person with this issue - I've run into it with context as well.

Is there any resources that you know of that discuss this issue further?

@JeremyGrieshop

This comment has been minimized.

Copy link

commented May 30, 2019

If you're in my boat, you've added a react component package from a local directory, and now it automatically builds and install it, along with its own copy of node_modules (because it uses npm link to do this), giving your app 2 copies or React now.

I've worked around it by deleting the node_modules/<my_package>/node_modules before running the app. To do this automatically:

"prestart": "rimraf ./node_modules/<my_package>/node_modules"

@antonybudianto

This comment has been minimized.

Copy link

commented Jun 12, 2019

I also faced this when doing SSR.
If you use Lerna for locally testing your React library, you might add "react/react-dom/react-router" as peerDependencies on your library, but you shouldn't add them as devDependencies. (this makes it duplicate)
You can use node --preserve-symlinks so it can lookup to the parent repo which install the peerDependencies.
For jest, you need to add the parent repo path to "jest.moduleDirectories" option so Jest can resolve them

@wind4gis

This comment has been minimized.

Copy link

commented Jun 17, 2019

@apieceofbart it works for me, thx!

@mikeaustin

This comment has been minimized.

Copy link

commented Jun 22, 2019

I ran into this error when loading external React components, for example with JS modules. They are completely outside the project, loaded with dynamic import() (well jsonp for more support). To get around the issue, we pass/inject React as a property to each module. This works, but then each module is dependent on the containing React app. We're trying to remove dependencies.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.