3

I'm using React to import a function with a useState hook and that seems to break it. I have a version of react with hooks:

npm ls react => react@16.10.2
npm ls react-dom => react-dom@16.10.2

I can use components fine. When I include a hooks, I get the "Invalid hook call" screen.

In my library project I have:

/**
 * @class ExampleComponent
 */

import * as React from 'react'

import styles from './styles.css'

export default function ThingyDefault() {
  return <p>hi</p>
}

export type Props = { text: string }

export class ExampleComponent extends React.Component<Props> {
  render() {
    const {
      text
    } = this.props

    return (
      <div className={styles.test}>
        Example Component: {text}
      </div>
    )
  }
}

////////////////// THIS DOESN'T SEEM TO WORK //////////////////
export function Example() {
  // Declare a new state variable, which we'll call "count"
  const [count, setCount] = React.useState(0);

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

In my project that uses that library:

import React from 'react';
import './App.css';
import ThingyDefault, {ExampleComponent, Example} from 'thingy';

const App: React.FC = () => {
  return (
    <div>
    <ThingyDefault />
    <ExampleComponent text='hello' />

    {/* commenting this component out makes it work */}
    <Example />
    </div>
  );
}

export default App;

What am I doing wrong here?

Ugtemlhrshrwzf
  • 1,967
  • 1
  • 14
  • 13

4 Answers4

1

You are not adhering to the Rules of Hooks, specifically in your case, calling a hook from a standard javascript function.

Only Call Hooks from React Functions Don’t call Hooks from regular JavaScript functions. Instead, you can:

✅ Call Hooks from React function components. ✅ Call Hooks from custom Hooks (we’ll learn about them on the next page). By following this rule, you ensure that all stateful logic in a component is clearly visible from its source code.

Community
  • 1
  • 1
Drew Reese
  • 165,259
  • 14
  • 153
  • 181
  • I pasted from the official documentation: https://reactjs.org/docs/hooks-state.html – Ugtemlhrshrwzf Oct 17 '19 at 14:13
  • Looks like you are using typescript, so I'm a little unsure of the syntax, but it seems to me there's an issue with `export function Example()`, that it isn't being seen as a react functional component and instead just a plain old javascript function. Just a guess though. – Drew Reese Oct 17 '19 at 14:15
  • 1
    If you temporarily move the problem function to the same file as your consuming component, does it work then? That would tell us if its an importing issue. – Brian Thompson Oct 17 '19 at 14:18
  • Do libraries export functions with hooks in some special way? – Ugtemlhrshrwzf Oct 17 '19 at 14:24
  • May need to list `react` and `react-dom` as peer dependencies in your component library, but interested in other suggestions. – Drew Reese Oct 17 '19 at 14:28
  • I'm kinda leaning to the dependency issue as well. Are you able to do an `npm ls react` inside of the library project? – Brian Thompson Oct 17 '19 at 14:41
  • "peerDependencies": { "prop-types": "^15.5.4", "react": "^16.10.2", "react-dom": "^16.10.2" }, I rm -r node_modules for both the library and the consumer project and reinstalled. Still no luck. It does seem like a dependency issue, though. I ran `npm ls react` in both library and consumer and both got 16.10.2 – Ugtemlhrshrwzf Oct 17 '19 at 14:46
  • 1
    Got it. I used a repo to generate my component library: https://medium.com/@xfor/developing-publishing-react-component-library-to-npm-styled-components-typescript-cc8274305f5a and fixed the issues it created by updating all of the dependencies to the newest version with this https://stackoverflow.com/questions/16073603/how-do-i-update-each-dependency-in-package-json-to-the-latest-version and I finally got it to work with a component with hooks. Thanks for your help. I'll mark this as the answer to give points for the help given. – Ugtemlhrshrwzf Oct 17 '19 at 16:08
1

Since it seems to be a import/export issue, try changing your export to this:

const Example = () => {
  // Declare a new state variable, which we'll call "count"
  const [count, setCount] = React.useState(0);

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}
export { Example };
Brian Thompson
  • 13,263
  • 4
  • 23
  • 43
1

You're not doing anything wrong there. It should work as intended. Check the example Stackblitz here, with the same version of React that you have.

I might re-check the app for any duplicate dependencies messing up the functionality of the hooks. Especially, it's unable to determine that your function Example(), is indeed a functional component.

Vandesh
  • 6,368
  • 1
  • 26
  • 38
0

I know that it is a bit old question but I had the same problem. Instead of using boilerplate I figured out what is the problem.

Generally it is necessary to point in rollup.config.js which modules are external. Eg.:

import resolve from "@rollup/plugin-node-resolve";
import commonjs from "@rollup/plugin-commonjs";
import typescript from "@rollup/plugin-typescript";
import dts from "rollup-plugin-dts";
import postcss from "rollup-plugin-postcss";
import tailwindcss from 'tailwindcss';
import autoprefixer from 'autoprefixer';
import packageJson from "./package.json" assert { type: "json" };

export default [
  {
    input: "src/index.ts",
    external: Object.keys(packageJson.peerDependencies || {}),
    output: [
      {
        file: packageJson.main,
        format: "cjs",
        sourcemap: true,
      },
      {
        file: packageJson.module,
        format: "esm",
        sourcemap: true,
      },
    ],
    plugins: [
      resolve(),
      commonjs(),
      typescript({ tsconfig: "./tsconfig.json" }),
      postcss({
        plugins: [
          tailwindcss(),
          autoprefixer()
        ],
      }),
    ],
  },
  {
    input: "dist/esm/types/index.d.ts",
    output: [{ file: "dist/index.d.ts", format: "esm" }],
    plugins: [dts()],
    external: [/\.css$/],
  },
];

So the most important lines in above configuration are:

import packageJson from "./package.json" assert { type: "json" };
[...]
external: Object.keys(packageJson.peerDependencies || {}),
BąQ
  • 296
  • 1
  • 3
  • 13