npm 模块上的无效钩子调用

Posted

技术标签:

【中文标题】npm 模块上的无效钩子调用【英文标题】:Invalid hook call on npm module 【发布时间】:2020-08-28 04:01:52 【问题描述】:

首先链接到回购:https://github.com/vmarchesin/react-konami-code

您应该能够构建它(确保取消注释src/index.js 上的导出)并链接到您的项目。也可以使用npm i -S react-konami-code@2.0.0-beta.0 进行尝试并获取钩子的错误。我把 2.0.0-beta.1 的钩子去掉了,因为它坏了。

问题描述

我为我的 npm 模块创建了一个自定义钩子,但在发布或将其用作模块后它不起作用。我怀疑问题在于 webpack 如何捆绑它,但我无法解决它。

采取的步骤

我确保在我的 webpack 配置中将 reactreact-dom 声明为外部变量。
externals: [
  
    react: 
      root: 'React',
      commonjs2: 'react',
      commonjs: 'react',
      amd: 'react',
    ,
    'react-dom': 
      root: 'ReactDOM',
      commonjs2: 'react-dom',
      commonjs: 'react-dom',
      amd: 'react-dom',
    ,
  ,
],
package.json 中将reactreact-dom 声明为peerDependencies
"peerDependencies": 
  "react": "^16.13.1",
  "react-dom": "^16.13.1"
,
挂钩正在工作。如果我声明并使用它,它就可以工作。如果我从模块中导入它(或使用 npm 链接),它不会。这是钩子的代码:
import  useEffect, useState, useCallback  from 'react';

export default (action, 
  code = [38, 38, 40, 40, 37, 39, 37, 39, 66, 65],
 = ) => 
  const [input, setInput] = useState([]);

  const onKeyUp = useCallback(
    (e) => 
      const newInput = input;
      newInput.push(e.keyCode);
      newInput.splice(-code.length - 1, input.length - code.length);

      setInput(newInput);

      if (newInput.join('').includes(code.join(''))) 
        action();
      
    ,
    [input, setInput, code, action],
  );

  useEffect(() => 
    document.addEventListener('keyup', onKeyUp);
    return () => 
      document.removeEventListener('keyup', onKeyUp);
    ;
  , [onKeyUp]);
;
我没有在类组件中调用钩子。

我正在使用webpack@4.43.0 以及babel@7babel-loader@8.1.0

这是我为 webpack 构建导出索引文件的方式:

import Konami from './Konami';

export default Konami; // disregard this, it works
export  default as useKonami  from './useKonami'; // this doesn't work

这是screenshot 和错误记录:

未处理的运行时错误错误:无效的挂钩调用。钩子只能是 在函数组件的主体内部调用。这可能发生 出于以下原因之一:

    您可能有不匹配的 React 版本和渲染器(例如 React DOM) 您可能违反了 Hooks 规则 您可能在同一个应用中拥有多个 React 副本

【问题讨论】:

我尝试了github.com/facebook/react/issues/16029 提供的大多数解决方案,但没有任何效果。 你能解决这个问题吗,因为我也遇到了同样的错误。 @Optimus 如果仍然需要,请在下面尝试我的答案。 【参考方案1】:

我遇到了同样的问题,尝试了所有解决方案,但都没有奏效。

解决方案是,我们需要将 Webpack 配置为基本上忽略 React 作为生产包的一部分以及对等依赖项。

为此,请在webpack.config.js 中添加以下行:

externals: 
   "react": "commonjs react",
   "react-dom": "commonjs react-dom",

【讨论】:

【参考方案2】:

尝试从我构建的 npm 包中导入反应组件时,我遇到了相同的错误消息。正如错误消息的解释所暗示的那样,这可能是duplicate React 的情况。

关于错误消息的重要部分是将reactreact-dom捆绑为外部导入,以便使用父项目的react,而不是使用第二个,这是一个重复的 React

我通过使用 rollup.js 构建我的 npm 包解决了这个问题,基本上遵循这个 guide。

我生成的 rollup.config.js:

import pkg from './package.json'
import babel from '@rollup/plugin-babel';

export default 
  input: 'src/index.js',
  output: [
    
      file: pkg.main,
      format: 'cjs',
      exports: 'named',
      sourcemap: true,
      strict: false
    
  ],
  plugins: [babel( babelHelpers: 'bundled' )],
  external: ['react', 'react-dom']

您的 npm 包中需要以下开发依赖项(假设您还必须使用 babel 进行转译):

npm i -D rollup babel-core @rollup/plugin-babel

你可以像这样捆绑汇总:

// add that to your package.json
"scripts": 
  "build": "rollup -c",
  "start": "rollup -c -w"  // for watching changes
,

使用 npm link 甚至可以在父项目中为您正在开发的打包的 react 组件进行热重载,而无需任何 npm 发布。耶。

【讨论】:

对我来说也是“重复反应”问题。将 react 和 react-native 从 dependencies/devDependencies 移动到我单独的 NPM omponent/package 中的 peerDependencies 中,一切都很好(然后删除所有 node_modules,运行 yarn 等)。【参考方案3】:

我在这里看到两个问题,这可能会导致您在控制台上看到的错误:

    不导入 React 所以破坏了 Rules of Hooks - Only Call Hooks from React Functions。 此外,问题可能来自您这里没有命名组件。

所以我将React 包含在以下内容中:

import React,  useEffect, useState, useCallback  from 'react';

你也可以尝试如下导出:

export default function ComponentName (action, 
  code = [38, 38, 40, 40, 37, 39, 37, 39, 66, 65],
 = ) 
   // your component code

或者如果你还想使用箭头功能,那么:

const ComponentName = (action, 
  code = [38, 38, 40, 40, 37, 39, 37, 39, 66, 65],
 = ) => 
   // your component code


export default ComponentName;

您可以参考这篇文章:Do not use anonymous functions to construct React functional components

我希望这会有所帮助!

【讨论】:

您好,感谢您的见解。我添加了React 作为导入的一部分,并将匿名箭头函数替换为默认导出的命名函数。清除 node_modules(模块和测试应用程序)并重新创建 npm link 后,错误仍然存​​在。 @vmarchesin 如果仍然需要,请尝试我在下面的回答中建议的解决方案。

以上是关于npm 模块上的无效钩子调用的主要内容,如果未能解决你的问题,请参考以下文章

样式组件和 Webpack 5 联合模块之间的冲突(无效的钩子调用)

反应钩子:无效的钩子调用

“无效的钩子调用。只能在函数组件的主体内部调用钩子”问题

无效的钩子调用错误:只能在函数组件的主体内部调用钩子

为啥 Jest 抛出“无效的钩子调用”?

反应库中的反应钩子给出无效的钩子调用错误