react: Hooks don't work with yarn link

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

What is the current behavior? When developing an external library locally and using yarn link to link the library to a local react app the “hooks can only be called inside the body of a function component” error comes up. However, after publishing to npm and using the published version in the local react app everything works as expected.

If the current behavior is a bug, please provide the steps to reproduce and if possible a minimal demo of the problem. Your bug will get fixed much faster if we can run your code and it doesn’t have dependencies other than React. Paste the link to your JSFiddle (https://jsfiddle.net/Luktwrdm/) or CodeSandbox (https://codesandbox.io/s/new) example below:

  1. Create a library that’s built with hooks (my-hooks-lib)
  2. Create a local app that uses the library (my-react-app) using CRA
  3. yarn link in my-hooks-lib and in my-react-app run yarn link my-hooks-lib

What is the expected behavior? yarn start in the react app should use hooks and render normally

Which versions of React, and which browser / OS are affected by this issue? Did this work in previous versions of React? react and react-dom 16.7.0-alpha.2 OSX

About this issue

  • Original URL
  • State: closed
  • Created 6 years ago
  • Reactions: 38
  • Comments: 38

Commits related to this issue

Most upvoted comments

Workaround:

cd PACKAGE_YOU_DEBUG_LOCALLY
yarn link
yarn install
cd node_modules/react
yarn link
cd ../../node_modules/react-dom
yarn link
cd YOUR_PROJECT
yarn link PACKAGE_YOU_DEBUG_LOCALLY
yarn link react
yarn link react-dom

I find it easier than some webpack setup or using yet another package manager (yapm? yanc? yalc? wut?)

If your React app is bundled with Webpack, you can use a Webpack alias to force all React references to resolve to a single module. In your webpack.config.js:

{
    /* ... */
    module: {
        rules: [ /* ... */ ],
    },
    resolve: {
        alias: { react: require.resolve("react") }
    },
}

(Solution from https://github.com/webpack/webpack/issues/8607#issuecomment-453068938)

Is there a workaround for this? How can people locally test react components they want to turn into libraries? :\


Workaround is https://github.com/whitecolor/yalc

It’s not expected that it would work unless you link react back from your module to your app.

This has actually always been the case (React apps are subtly broken when there are two copies of React module). Hooks surface this immediately which I guess is good.

We do have another issue tracking a better error message for this case.

Thanks @JerryGreen!

For future explorers, the above solution didn’t happen to work in my case. Not sure if it had to do with the use of NextJS, but that is possible!

A slight tweak worked, though: making the package link to my project’s react and react-dom.

If you are in the same situation, perhaps try:

cd YOUR_PROJECT
cd node_modules/react
yarn link
cd ../react-dom
yarn link

cd PACKAGE_YOU_DEBUG_LOCALLY
yarn link
yarn install
yarn link react
yarn link react-dom

cd YOUR_PROJECT
yarn link PACKAGE_YOU_DEBUG_LOCALLY

This did the trick for us!

Those who still struggling with the issue and work with react-scripts without eject do the following to solve the issue:

  1. yarn add @craco/craco
  2. Change the package.json start script

From:

   "start": "react-scripts start",

To

   "start": "craco start",
  1. Add craco.config.js file to your root application with the following content:
const path = require('path')

module.exports = {
    webpack: {
        alias: {
            react: path.resolve(__dirname, './node_modules/react'),
            'react-dom': path.resolve(__dirname, './node_modules/react-dom'),
        },
    },
}

React is great for components but it looks like everything around react is preventing you from creating components. Configuring bundlers and npm dependencies to work with external components is really a hell. And with CRA this is almost impossible. I beg you, please find a solution to this ! Maybe from webpack 5 module federation ?

I configured this in webpack 5 and it’s working for me:

module.exports = {
  //...
  resolve: {
    symlinks: false,
  },
}

Workaround:

cd PACKAGE_YOU_DEBUG_LOCALLY
yarn link
yarn install
cd node_modules/react
yarn link
cd ../../node_modules/react-dom
yarn link
cd YOUR_PROJECT
yarn link PACKAGE_YOU_DEBUG_LOCALLY
yarn link react
yarn link react-dom

I find it easier than some webpack setup or using yet another package manager (yapm? yanc? yalc? wut?)

It works! Tks!

Nothing from the above was needed! I got mine fixed by just keeping react and react-dom only as peerDependencies in package.json and delete all other occurrences. After that delete node_modules and package-lock.json and install again. Then in should work.

Most often, you cannot do this during development though, as you may need React for unit testing or 3rd party tools like Storybook. In a mono-repo, even if React is a peer-dependency of your package, it will fetch the “closest” version, so from your monorepo main node_modules instead of those from the app. I’ve tested Yalc, with some success at the moment, though I am still not sure of the precise differences with a “normal” npm install. This is how we intend to solve this for good in the future, taking inspiration from Meteor solution for this issue, based on an NPM_PACKAGE_DIRS environment variable: https://github.com/VulcanJS/vulcan-next/issues/104

Nothing from the above was needed! I got mine fixed by just keeping react and react-dom only as peerDependencies in package.json and delete all other occurrences. After that delete node_modules and package-lock.json and install again. Then in should work.

@jaguardo in my experience Yalc removed a ton of the complexity that you would otherwise have to manage with symlinks yourself. Have been using it for a year or so now and the quality is very high. Worth giving it a shot for your use case.

I still get this error message even though I linked react and react-dom (via yarn link) and set react and react-dom as alias in my webpack.config:

'react': require.resolve('./node_modules/react'),
'react-dom': require.resolve('./node_modules/react-dom'),

Will this be addressed somehow? I guess there is an workaround - but when you use create react app you can’t really do any hacks in the webpack.config.js 😄

If you’re having this issue when developing a package that exports a React component, and you have a folder structure like this:

react-component-package/
  package.json
web-application/
  package.json

Assuming react-component-package has React as a dev and peer dependency, you can run this command from the react-component-package folder: npm link ../web-application/node_modules/react

The same command should work with Yarn: yarn link ../web-application/node_modules/react

https://stackoverflow.com/a/58612244

I had the same problem in a Next.js app and solved the issue with the approach suggested by dcecile. I added this entry in next.config.js:

webpack: (config) => {
  config.resolve.alias = {
    ...config.resolve.alias,
    react: path.resolve(__dirname, './node_modules/react'),
  };
  // Important: return the modified config
  return config;
},

Here are the docs for modifying Next.js webpack configuration.

Hey @dcecile thanks for a quick answer, sadly, that doesn’t work for me as well.

yalc is the only solution to this problem (and the myriad of other issues caused by yarn/npm link) that consistently works.

Its pretty troubling that both yarn and npm seem convinced that naively symlinking entire directories ever worked.

Nothing worked but yalc for me

Currently there are like 3 ways you can install local dependencies,

  1. yarn link
  2. yarn add <repository name>
  3. yarn add .tgz file

npm pack on the library repository and yarn add .tgz file in the other repo seems to work for me. The other two give me the hook error.

Currently there are like 3 ways you can install local dependencies,

  1. yarn link
  2. yarn add
  3. yarn add .tgz file

npm pack on the library repository and yarn add .tgz file in the other repo seems to work for me. The other two give me the hook error.

perfect.

yarn add .tgz file

Thanks @Blkc

cd YOUR_PROJECT
cd node_modules/react
yarn link
cd ../react-dom
yarn link

cd PACKAGE_YOU_DEBUG_LOCALLY
yarn link
yarn install
yarn link react
yarn link react-dom

cd YOUR_PROJECT
yarn link PACKAGE_YOU_DEBUG_LOCALLY

I had problems using this workaround with React 18. The error was:

Module not found: Error: Can't resolve 'react-dom/client'

To make it work I had to downgrade to a previous React version.

Here the steps I followed:

  • go to package.json, and change the versions of both react and react-dom to:
    "react": "^17.0.2",
    "react-dom": "^17.0.2",
  • change index.js to:
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';

ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
  document.getElementById('root')
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

  • yarn install (I also deleted the node_modules folder before this, but it should not be necessary)
  • then follow the steps of the original workaround

I hope this can help in case someone ends up having the same issue.

Workaround:

cd PACKAGE_YOU_DEBUG_LOCALLY
yarn link
yarn install
cd node_modules/react
yarn link
cd ../../node_modules/react-dom
yarn link
cd YOUR_PROJECT
yarn link PACKAGE_YOU_DEBUG_LOCALLY
yarn link react
yarn link react-dom

I find it easier than some webpack setup or using yet another package manager (yapm? yanc? yalc? wut?)

@jerrygreen thanks dude. You saved us. Works like a charm!

Yes. Works like a charm, currently.

But someday, when you rm -rf node_modules or move the folder,

you will find, wow, something is screwed like a ****!

In YOUR_PROJECT’s package.json add into a jest config section:

...
  "jest": {
    "moduleNameMapper": {
        "^react$": "<rootDir>/node_modules/react",
        "^react-dom$": "<rootDir>/node_modules/react-dom"
    }
  }
...

It worked for me

For those coming to the issue via a monorepo such as lerna, you can solve the problem by hoisting the dependencies to the root level.

https://github.com/facebook/react/issues/15097