Open brunolemos opened 5 years ago
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.
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.
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
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"
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: https://github.com/facebook/react/issues/2402
@mpeyper It works. Thanks
@apieceofbart That worked for me. Thanks for the suggestion. 👍
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).
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!
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.
I fixed my issue by removing the caret in "react": "^16.7.0-alpha.2"
Here is the full comment: https://github.com/facebook/react/issues/14454#issuecomment-449585005
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"
},
Same here!!
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.
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!
@taylorham The link in the commit doesn't point to anything yet. I'll wait for it, but this is an issue I have been having since using hooks in a linked (as of npm link
) package and it's impossible to work with it locally without publishing.
After looking severals issues, I tought this was an issue with react-hot-loader that was compiling components to classes, but even after they released a version with Hook support, it still fails the same way.
I've tried a lot of different hacks but no luck. I don't know why everybody hasn't been struck with this issue yet 🧐
@dotlouis Yeah, it's just an updated error message so far and the issue itself is still a pain.
The only thing that has worked for me at all is to make whatever app I'm developing depend on the library's instance of React by using "react": "link:../my-library/node_modules/react"
.
[ok] for me, correction was not about package.json or others double react cause : i had a global themeProvider on top of my app, coming from context. Replacing it with a "useContext Hook" ( while rewriting it as a functional comp ) seemed to be the only solution Maybe is there an issue when
<GoodOldContext iam={a class component}>
<BrandNewHook>
errors : Hooks can only be called inside the body of a function component #35
</BrandnewHook>
</GooOldContext>
export withGoodOldContext.consumer(here component)
I'm developing a component where there is an example
folder that uses create-react-app
.
Doing this in package.json
resolved this issue for me:
{
...
"dependencies": {
"my-component": "link:..",
"react": "link:../node_modules/react",
"react-dom": "link:../node_modules/react-dom",
"react-scripts": "2.1.3"
},
...
}
@taylorham @DylanVann Thanks for your input guys. Unfortunately, it still does not work for me.
And I could not find any documentation about this link:
protocol you used.
Basically, it says that "react-spring" (another dep that also uses react as a dependency) cannot find react-dom
. Can you point me to some documentation about "react": "link:../some/path"
please?
I am using linked UI package as well and I was able to fix this issue. You need to export react renderToString from UI (linked package). I created render function in the linked package.
Just for a better context - https://github.com/facebook/react/issues/14257
Thanks @theKashey. @gaearon seems to think that it is the normal behavior. I get that React should not be loaded twice, but what is the recommended way of working with a linked local package then?
I also had issues with Lerna workspaces getting symlinked properly. This was the trick I used to get this to work. Be sure to run npm install
afterwards.
"dependencies": {
"react-dom": "file:../common/node_modules/react-dom",
"react": "file:../common/node_modules/react"
}
There is many ways to solve it, and yarn resolutions would not usually help - it's more related to the "building tool"
aliases
- just "hard" alias everything ducks like react
to a single file
resolve: {
extensions: ['.ts', '.tsx', '.js', '.jsx'],
alias: {
react: path.resolve(path.join(__dirname, './node_modules/react')),
}
import {setAliases} from 'require-control';
setAliases({
'react': path.resolve(path.join(__dirname, './node_modules/react'))
});
moduleNameMapper
"jest": {
"moduleNameMapper": {
"^react$": "<rootDir>/node_modules/$1",
},
@theKashey thanks for your insights, this makes sense when we consider how module resolution is done (bottom, then up the tree), but from a user point of view I don't find this very practical. When I npm link
a package, I would expect it to work without having to re-wire dependencies explicitly. This makes developing a package locally quite painful.
This is a cornerstone, this is how node_modules
designed to work, this is why you might have two versions of a Button
in two different major versions, and dependent modules will easely find the "right" version of a package.
This is how it should work all the time.
Node.js
internals are quite straightforward - try to open a file adding all known prefixes (like node_modules
) or extensions(like js
, ts
, json
); if none found go one directory up. The only way to "fix it" - replace nodejs module resolution system.
yarn pnp
will do that, and might solve the problem. yarn workspaces
, which might hoist shared packages to the top - will also solve the problem without any "magic" involved.
npm workspaces
? Does not exists right now.
I actually ended up switching my project to use workspaces. It resolves this without having to use resolutions, and the hoisting/structure is beneficial anyways.
This one was a headscratcher. I tried the webpack resolve.alias setting but it was not working, tried many settings too but never really managed to get it to work unfortunately, but here's how I finally managed to get it to work:
Here's my folder structure:
Project | +-- node_modules |
---|
+-- build | +-- index.js |
---|
+-- example (create-react-app)
| |
| +-- package.json
I had to modify my package.json inside the example folder, essentially pulling react from the project's 'main' node_modules based on @jmlivingston's suggestion, here's how it end up:
"dependencies": {
"react": "file:../node_modules/react",
"react-dom": "file:../node_modules/react-dom",
"react-scripts": "2.1.5"
},
Now after that I ran npm install
and then I ran npm link
, that did the trick.
Hopefully this can help someone else and save some time.
So any fix to this issue? I've tried as many recommendations here as I can and no luck. I am using create-react-app and typescript. Using React/React-dom 16.8.3. This is a new project I created 2 days ago so pretty plain. I am using useSpring() and animated.div. Thanks
@guru-florida are you using react-router by any chance?
I'm using the same stack as you (typescript & create-react-app) and my issue with with the render
attribute. Changing it to component
did the trick.
Before:
<Route path="/signup" render={SignUp} />
After:
<Route path="/signup" component={SignUp} />
Hope it helps..!
@mikeyyyyyy No, not using React Router in this one. Thanks for the tip though cuz I was in the last project I tried using spring and had the same issue.
I had this issue with npm link (in a parcel app), the npm link .../whatever/node_modules/react
doesn't seem to resolve it, works fine with non-hook components though
@tj I guess you have problem with ssr. Fast workaround is to export react functions or whole react from linked package and import it in your server package
@seeden ahh I'm not using SSR, just a SPA w/ Parcel. I have a ui
pkg internally for my own stuff and an app I'm working on, both have the same react
version, seems odd that there's a duplicate but maybe that's a Parcel concern
@tj oh, I see. Then good luck with this very strange issue. I spent one week with this
So any fix to this issue?
There is no issue here per se. As explained on this page, React needs useState()
calls to be on the same react
object as the react
object as "seen" from inside react-dom
. If that's not what happens for you, it means you're bundling two copies of React on the page — which is bad by itself and also breaks some other features before Hooks. So you'll want to fix it anyway. This page contains common ways to diagnose to fix it.
We're leaving this discussion open for sharing particular workarounds when people run into this problem. But it's not an issue per se that can be "fixed" by anyone but you.
I had this issue with npm link (in a parcel app), the npm link .../whatever/node_modules/react doesn't seem to resolve it, works fine with non-hook components though
Do you mind creating a small repro case?
@gaearon will do, should have time to dig in a bit more next week
Happily, require-control
has fixed our issue with yarn link
+ SSR + styled-components 4's static context. Thanks @theKashey 👍
I tried everything here and failed. It was actually something different not documented here. It was to do with the case sensitivity of the react imports. In some cases we had:
import React from 'react'
And in others:
import React from 'React'
On some file systems (unix, osx) this causes Webpack to instantiate two copies of React.
This caused extreme confusion as I could clearly see we only have one copy of React; but it was instead the way we were importing it.
The test on the react documentation also comes out fine as it obviously uses only lower case.
This sounds like it could be worthy of a mention in the docs?
For me the reason of multiple instances of React was Webpack DllPlugin. For my vendor DLL I didn't include react
and react-dom
to my the entries list, however, I had other libraries which required react
or react-dom
so my DLL contained react
and react-dom
(quick check of the manifest json file can reveal that). So, when I was running the code, and import React into the application it was loading it from node_modules
, but in the vendors' code React was required from their DLL file.
Overall: be careful with DLL files and make sure your included modules don't include extra dependencies that you don't need otherwise you will double import them.
I was able to fix this by updating react-hot-loader
to 4.6.0
this did the trick for the npm link
stuff in Parcel:
"alias": {
"react": "../ui/node_modules/react",
"react-dom": "../ui/node_modules/react-dom"
}
not sure if that's what it'll try to use for a production build, seems kind of hacky but it works for development at least
@theKashey OMG man, it works! I've tried many different solutions that people suggests related to this issues: mangling with package.json
deps, tracing "two reacts" across project, checking if I'm breaking the *rule of hooks` (which I'm not), but I think that your option with:
alias: {
react: path.resolve(path.join(__dirname, './node_modules/react')),
'react-dom': path.resolve(path.join(__dirname, './node_modules/react-dom'))
}
allows us to move our project to the next lvl, using hooks in our app-as-a-lib.
This is the resulted webpack.config.js
npm ls react
returns
web@0.0.0 D:\code\project
`-- (empty)
for me
console.log(window.React1 === window.React2)
returns true
at this point i'm thinking it's SSR causing the issue
Update. It was indeed caused By React-apollo's SSR behaviour (https://github.com/apollographql/react-apollo/issues/2541) Upgrading to 2.3.1 fixed it
Hi guys, our team face this problem and took few days to sort it out.
Solution A: specify the package position to look for, as mentioned above
alias: {
react: path.resolve(path.join(__dirname, './node_modules/react')),
'react-dom': path.resolve(path.join(__dirname, './node_modules/react-dom'))
}
Solution B: use webpack resolve.modules to prioritise the right node_modules folder to look for modules
First thing first, it's not react's fault, it's not even lerna's, but react, webpack, and npm-link might need to take some responsibilities.
-Non-monorepo:
Structure
- mono repo root
- packages
- ComponentWithHooks (peerDependency: react@^16.8.1)
- ProductA (dependency: ComponentWithHooks, dependency: react@^16.8.4)
- ProductB (dependency: react@^16.8.1)
Once bootstrap with workspaces, it will resolve to
- mono repo root
- node_modules
- react(16.8.1)
- packages
- ComponentWithHooks
- node_modules (empty)
- ProductA
- node_modules
- react(16.8.4)
- ProductB
- node_modules (empty)
And once you serve ProductA
with webpack or maybe something else, it will contain 2 react instances.
Code in ProductA, will looks for ProductA/node_modules/react
.
But the imported ComponentWithHooks will look for mono repo root/node_modules/react
.
Why? Remember the look up rules of npm? If it cannot find the module in it's own node_modules folder, it will look for parent's node_modules...
So tools like webpack applied this rule in default perfectly.
It's nothing wrong util mono repo solution become popular.
And normal package won't notice this as most of them do not require single instance as react and redux.
I'm having this same issue using a very basic reproduction using yarn workspaces example - https://github.com/mwarger/yarn-workspace-hooks-repro
I have a component-library
that is written in typescript and bundled with parcel. The example-demo
is what will showcase this component-library
and is a freshly created CRA app. All common packages are hoisted with yarn, so in theory there should only be one version of react available. However, the React.useEffect
call I'm making in index.tsx causes the error that leads me to this GitHub issue.
Everything works until a hook is added. To reproduce the error, uncomment lines 7-9 in component-library/src/index.tsx
Hopefully I'm doing something silly that I have overlooked. Please advise as to any steps I may use to try and remedy this. Thank you!
Follow-up Edit: The below suggested debug script output prints true
for me. It appears that I do not have two Reacts.
// Add this in node_modules/react-dom/index.js
window.React1 = require('react');
// Add this in your component file
require('react-dom');
window.React2 = require('react');
console.log(window.React1 === window.React2);
Took me several hours so it might worth to take a note here.
In my case, I put a line of <script defer src="./dist/bundle.js" />
in the head of the HTML template, which works as normal when not using React hooks. All the solutions don't work and the window.React1 == window.React2
check returns true
in this case.
Since webpack will inject the script tag afterward, the template should not have script tag by its own. Remove the script tag from the template make React functional with hooks (pun intended) again.
In my case I have a React app which was npm linked to a dependency I was working on. This will do the trick until I can fix a couple dependencies that need to move react
and react-dom
to dev and peer deps.
cd node_modules/react && npm link
cd node_modules/react-dom && npm link react
npm link react
Why does it work? The error warning page mentions that "in order for Hooks to work, the react import from your application code needs to resolve to the same module as the react import from inside the react-dom package".
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.