jaredpalmer / tsdx

Zero-config CLI for TypeScript package development
https://tsdx.io
MIT License
11.22k stars 507 forks source link

"Invalid hook call" error when importing a TSDX library with styled-components #950

Open robertovg opened 3 years ago

robertovg commented 3 years ago

Current Behavior

I'm building a private library with tsdx and would like to add styled-components there. When I do it, they work within the tsdx project but when I import those components in other projects (next.js/CRA using already styled-components inside), I get the following error:

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.
Screen Shot 2020-12-24 at 4 43 56 PM

Expected behavior

I would like to be able to use those styled-components exactly like any other react, or plain js function I'm able to use with tsdx, so they can be reused in my projects.

Especially when the test for those styled-components works and they get rendered correctly inside storybook.

Suggested solution(s)

Please, provide a concise way to enable tsdx-styled-components to use styled-components and export them correctly 🙏.

Additional context

I isolated this problem in a small project and prepared the steps to reproduce it:

  1. Create an empty project

    npx tsdx create tsdx-styled-components

    And uploaded to Github

  2. Add styled-components to it and a small component exported in the index

    With the second commit I added styled-components to the project, they get render correctly in storybook and test pass correctly for those styled-components components.

  3. Create a new next.js project and consume the library locally

    • Create a new project with yarn create next-app --example with-typescript next-ts-styled-components-tsdx-host.
    • Then yarn build the tsdx-styled-components.
    • yarn add ../tsdx-styled-components in next-ts-styled-components-tsdx-host (just to use it locally before registering)
    • Modify the pages/index.tsx file to use the new library:

      import Link from "next/link";
      import { Thing, SimpleStyledH1Application } from "tsdx-styled-components";
      import Layout from "../components/Layout";
      
      const IndexPage = () => (
      <Layout title="Home | Next.js + TypeScript Example">
       <Thing></Thing>
       {/* This causes the break, because is a styled-component */}
       <SimpleStyledH1Application />
       <h1>Hello Next.js 👋</h1>
       <p>
         <Link href="/about">
           <a>About</a>
         </Link>
       </p>
      </Layout>
      );
      
      export default IndexPage;

      I'm creating a new project just to verify, but we get the same error in our CRA, next.js projects with styled-components configured.

      SimpleStyledH1Application causes the error, if you comment it, the page renders correctly.

  4. Add the workaround described in https://github.com/formium/tsdx/issues/543 doesn't work.

    With the third commit,add styled-components/macro version and adds babel-plugin-macros to .babelrc as described https://github.com/formium/tsdx/issues/543 & https://github.com/formium/tsdx/pull/644.

Your environment

 System:
    OS: macOS 11.1
    CPU: (16) x64 Intel(R) Core(TM) i9-9980HK CPU @ 2.40GHz
    Memory: 79.52 MB / 32.00 GB
    Shell: 5.8 - /bin/zsh
  Binaries:
    Node: 14.13.1 - /usr/local/bin/node
    Yarn: 1.22.10 - /usr/local/bin/yarn
    npm: 6.14.8 - /usr/local/bin/npm
  Browsers:
    Chrome: 87.0.4280.88
    Chrome Canary: 89.0.4366.0
    Firefox Developer Edition: 83.0
    Safari: 14.0.2
zoltan-nz commented 3 years ago

Hi @robertovg

Are you using yarn link or npm link to link your app with your library?

A similar issue is discussed here: https://github.com/facebook/react/issues/13991

A possible solution with craco

Override create react app configuration if you use create react app scripts.

yarn add -D @craco/craco craco-alias
const CracoAlias = require('craco-alias');

// Source: https://github.com/facebook/react/issues/15315#issuecomment-638504372
module.exports = {
  plugins: [
    {
      plugin: CracoAlias,
      options: {
        source: 'options',
        baseUrl: './',
        aliases: {
          // described at https://github.com/facebook/react/issues/13991
          react: './node_modules/react',
          'react-dom': './node_modules/react-dom',
          'styled-components': './node_modules/styled-components',
        },
      },
    },
  ],
};

Reference: https://github.com/facebook/react/issues/15315#issuecomment-638504372

robertovg commented 3 years ago

Unfortunately, we are using next.js. I added the Craco config both to the host and to the library project, and I'm getting exactly the same problem. Because @zoltan-nz, the idea is to add this override of libraries to the host or library project?

Well, making some reading (https://github.com/vercel/next.js/issues/9022, https://github.com/vercel/next.js/issues/7626) looks like the wrong version of styled-components, react-dom, or react can cause this problem.

If you follow my instructions to create a host project you will see, tsdx compiles correctly the tsx but only the component without styled-components can be used there, so looks like is related to styled-component library conflict (but in the example described, I'm using same styled-component version).

I can upload the host project if this helps and I would really appreciate any further help with it. The idea is to use tsdx to create a library project to be used in next.js for our use case.

Thanks again in advance.

federico-qadra commented 3 years ago

We are having the same exact problem using Next.js. The link works fine if we use a webpack alias for CRA, but for Next.js there is no way to make it work.

Did anyone find a solution/fix?

robertovg commented 3 years ago

We are having the same exact problem using Next.js. The link works fine if we use a webpack alias for CRA, but for Next.js there is no way to make it work.

Did anyone find a solution/fix?

Good to know someone else is facing the same issue. I would really love to find a solution. I hope someone can put light on it, as otherwise, we can't have libs packages using tsdx.

kylepeeler commented 3 years ago

We are having the same exact problem using Next.js. The link works fine if we use a webpack alias for CRA, but for Next.js there is no way to make it work. Did anyone find a solution/fix?

Good to know someone else is facing the same issue. I would really love to find a solution. I hope someone can put light on it, as otherwise, we can't have libs packages using tsdx.

Same issue here... this really should be addressed, else it's our only blocker to us using tsdx

kylepeeler commented 3 years ago

I was able to fix this by using the solution in #543, which only seems to work if you don't need any other babel plugins?

robertovg commented 3 years ago

Sorry @kylepeeler, we tried this solution too and still got the same problem.

Just to allow people to recreate the issue, I just uploaded the host project: https://github.com/robertovg/next-ts-styled-components-tsdx-hosttsdx-styled-components

It will work out of the box, but as soon as we link tsdx-styled-components or create a symbolic link in the host to the tsdx-styled-components it won't..

Without that, we don't have any way to test our library in the host project before committing to Github (with this simple way of using as Git URL)

PD: Same behavior also using babel-plugin-styled-components instead of macro

robertovg commented 3 years ago

We found something interesting, maybe someone can explain this a bit more:

We have both projects:

we run the host and it works, and npm ls react has no conflicts

Then we yarn build & yarn link the tsdx-styled-components and then:

The funny thing is the two projects are using exactly the same react version and it's marked in peer dependencies.

Screen Shot 2021-01-13 at 4 31 32 PM

I hope the source of the problem it's more clear now.

robertovg commented 3 years ago

We found a dirty hack to make it work in local finally. As I said, it will work perfectly when the project is running with the default configuration in the projects, but when we want to run the lib project locally and see the changes in the host is when we face the problems.

We realized the problem was with duplication react version, meaning the host project has 2 copies of react, the default one /next-ts-styled-components-tsdx-host/node-modules/react and /next-ts-styled-components-tsdx-host/node-modules/tsdx-styled-components/node-modules/react and just deleting the later we get the project working, the problem is if we do that, then we can't successfully build the lib project (needs react).

So our solution finally is what the official React guide suggest:

This problem can also come up when you use npm link or an equivalent. In that case, your bundler might “see” two Reacts — one in application folder and one in your library folder. Assuming myapp and mylib are sibling folders, one possible fix is to run npm link ../myapp/node_modules/react from mylib. This should make the library use the application’s React copy.

In our case, having both projects in the same folder, in the tsdx-styled-components (lib project) we execute npm link ../next-ts-styled-components-tsdx-host/node_modules/react and makes everything work.

Not sure if there is a better way, but this works.

timosnel commented 3 years ago

Running into the same problem here with latest Next.js (10.0.5).

I refer to my package on the same filesystem in package.json as follows:

"dependencies": {
  "next": "10.0.5",
  "react": "17.0.1",
  "react-dom": "17.0.1",
  "my-package-name": "file:../my-package-name"
}
robertovg commented 3 years ago

Hey @timosnel , I pretty sure you only need to execute the same solution I posted,

cd my-package-name;
npm link ../your-host/node_modules/react

I hope it works also for you,

plotka commented 3 years ago

I was having the same issue (not with styled-components but with Ant design library) and I am also referencing my lib project on the same filesystem as @timosnel and I fixed it with npm link as @robertovg suggested. Thanks a lot!

I have a question about the production environment - from what I understand the link is created locally only, is that correct? How can I make it work in production if I am referencing my lib package from the same filesystem? @robertovg maybe you can help? Thanks

ilovett commented 3 years ago

@robertovg solution worked -- for me I had to pass --legacy-peer-deps as well:

npm link --legacy-peer-deps ../${HOST_PROJECT}/node_modules/react/
joshfarrant commented 3 years ago

@robertovg's solution worked for me as well, thanks! 🎉

OliverDudgeon commented 3 years ago

I had a similar problem. My solution was to add a "resolutions" object to my package.json (a yarn feature).

"resolutions": {
  "react": "^17.0.2"
}

N.B. I'm also using yarn workspaces which could also solve some problems with duplicate react versions as deps are hoisted to a single node_modules in the root.

gauravverma029 commented 3 years ago
Screen Shot 2021-05-17 at 11 30 58

Getting error with using next js latest version.what is final hack please share. i am using yarn workspace

webcodedsoft commented 1 year ago

This issue is kind of wired after 2 days of trying to resolve this issue with different suggestions and I was still unable to resolve it, but In my case, I was able to resolve this issue by removing react from peerDependencies in my package.json