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

Posted

技术标签:

【中文标题】为啥 Jest 抛出“无效的钩子调用”?【英文标题】:Why Jest throws "Invalid hook call"?为什么 Jest 抛出“无效的钩子调用”? 【发布时间】:2020-12-12 06:05:08 【问题描述】:

我有一个 Gatsby 网站和一个组件库。运行 Gatsby 站点时,我收到“无效的钩子调用”错误,添加 Webpack 别名(如建议的 here)解决了这个问题。

现在,我在使用 Jest 和 react-testing-library 运行测试时遇到了类似的“无效挂钩调用”错误:

Error: Uncaught [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 ... for tips about how to debug and fix this problem.]
          at reportException (/Users/misha/temp/invalid-hook-repro/basis/node_modules/jsdom/lib/jsdom/living/helpers/runtime-script-errors.js:62:24)
          at innerInvokeEventListeners (/Users/misha/temp/invalid-hook-repro/basis/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:333:9)
          at invokeEventListeners (/Users/misha/temp/invalid-hook-repro/basis/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:274:3)
          at htmlUnknownElementImpl._dispatch (/Users/misha/temp/invalid-hook-repro/basis/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:221:9)
          at HTMLUnknownElementImpl.dispatchEvent (/Users/misha/temp/invalid-hook-repro/basis/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:94:17)
          at HTMLUnknownElement.dispatchEvent (/Users/misha/temp/invalid-hook-repro/basis/node_modules/jsdom/lib/jsdom/living/generated/EventTarget.js:231:34)
          at Object.invokeGuardedCallbackDev (/Users/misha/temp/invalid-hook-repro/basis/node_modules/react-dom/cjs/react-dom.development.js:237:16)
          at invokeGuardedCallback (/Users/misha/temp/invalid-hook-repro/basis/node_modules/react-dom/cjs/react-dom.development.js:292:31)
          at beginWork$1 (/Users/misha/temp/invalid-hook-repro/basis/node_modules/react-dom/cjs/react-dom.development.js:23203:7)
          at performUnitOfWork (/Users/misha/temp/invalid-hook-repro/basis/node_modules/react-dom/cjs/react-dom.development.js:22157:12)

我尝试检查多个版本的 React:

$ npm ls react

└── react@16.13.1 

$ npm ls react-dom

└── react-dom@16.13.1 

而且,还尝试在jest.config.js 中别名react

moduleNameMapper: 
    react: "<rootDir>/website/node_modules/react",
    ...

但是,这会导致:

TypeError: React.createContext is not a function

有什么想法吗?

重现问题:

1. git clone https://github.com/moroshko/basis.git
2. cd basis
3. git checkout 52f1dc7
4. npm install
5. ./node_modules/.bin/cross-env BABEL_ENV=cjs ./node_modules/.bin/jest -- -t Playground

【问题讨论】:

@James 在这里很难指出具体的代码。请参阅包含的复制品。 问题应包含重现问题所需的所有信息。见***.com/help/mcve。你有一个 repo 很好,但它不能替代 MCVE。错误被截断,它通常包含所有必要的信息来缩小问题的范围,即哪个测试导致了它。这里不需要别名。 回购似乎没问题,无法重现问题。尝试重新安装两个 node_modules。如果网站/src 测试正在渲染 React,则可能会出现问题,在这种情况下,reactreact-dom 可能需要映射到根 node_modules(而不是您尝试过的 website/node_modules)——但它们没有。 @EstusFlask 它应该会失败。您可以在此处查看 CI 中的失败:github.com/moroshko/basis/runs/1018168495。此外,将react 映射到&lt;rootDir&gt;/node_modules/react 并没有任何区别。 在那里你可以清楚地看到失败的网站/src/pages/playground/index.test.js,你可以在发布时将问题缩小到这个测试。 master 中没有 playground/index.test.js。正如我所说,至少reactreact-dom 都应该被映射(也可能是其他包),否则这是一种得到错误的行之有效的方法。映射应该类似于 "^react($|/.+)": "&lt;rootDir&gt;/node_modules/react$1",与 react-dom 相同。 【参考方案1】:

来自@EstusFlask 在 cmets 中提供的解决方案

在 package.json 的“jest”部分下,或在您的 jest.config.js 文件中,无论您以何种方式配置 jest,添加一个带有 "^react($|/.+)": "&lt;rootDir&gt;/node_modules/react$1" 的“moduleNameMapper”部分。

我的 jest.config.js 看起来像这样:

module.exports = 
  moduleNameMapper: 
    "^.+\\.(css|less)$": "<rootDir>/__mocks__/styleMock.js", // replaces .css imports with an empty object
    "\\.(jpg|png|gif|ttf|eot|svg)$": "<rootDir>/__mocks__/fileMock.js", // replaces file imports with a useless string
    "^react($|/.+)": "<rootDir>/node_modules/react$1", // makes sure all React imports are running off of the one in this package.
  ,
  setupFiles: [
    "<rootDir>/jest/setEnvVars.js",
    "<rootDir>/jest/mockLocalStorage.js",
  ],
  verbose: true,
;

我遇到了同样的错误,这解决了我的问题。

再次感谢@EstusFlask。

【讨论】:

以上是关于为啥 Jest 抛出“无效的钩子调用”?的主要内容,如果未能解决你的问题,请参考以下文章

react-intl 与 react-testing-library 给出了无效的钩子调用错误

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

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

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

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

Material UI 无效的钩子调用