解决在同一个应用程序中有多个 React 副本

Solve having more than one copy of React in the same app

我正在本地开发一个 React 模块。为此,我使用 npm link 链接我的模块。 模块已成功导入,但挂钩在模块内部失败。它抛出以下错误:

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.

检查 React docs 中的建议,我可以确认我的应用程序正在使用 React 的重复版本,因为以下代码 returns false:

// node_modules/mymodule/src/index.js
export { default as ReactFromModule } from 'react'
// src/index.js
import React from 'react'
import { ReactFromModule } from 'mymodule'
console.log(React === ReactFromModule) //false

This issue 充满建议,但令人困惑。我该如何解决?

注意:我没有违反钩子规则,错误仅在从应用程序导入模块时出现。

在您正在开发的模块中,将冲突的包添加到peerDependencies(并将它们从dependenciesdevDependencies中删除):

  // package.json
  "peerDependencies": {
    "react": "16.13.1",
    "react-dom": "16.13.1"
  },

在您的模块中执行 npm install

现在将它们作为 externals 添加到 你的模块 的 webpack 配置中。这些包不应包含在模块的捆绑包中(使用该模块的应用程序将提供它们):

// webpack.config.js
module.exports = {
    /*
    rest of config...
    */
    output: {
        filename: "index.js",
        pathinfo: false,
        libraryTarget: 'umd', // In my case, I use libraryTarget as 'umd'. Not sure if relevant
    },
    externals: {
        // Use external version of React
        "react": {
            "commonjs": "react",
            "commonjs2": "react",
            "amd": "react",
            "root": "React"
        },
        "react-dom": {
            "commonjs": "react-dom",
            "commonjs2": "react-dom",
            "amd": "react-dom",
            "root": "ReactDOM"
        }
    },
};

然后,在构建您的模块后,在您的应用程序中您可以检查两个版本现在是否相同:

// node_modules/mymodule/src/index.js
export { default as ReactFromModule } from 'react'
// src/index.js
import React from 'react'
import { ReactFromModule } from 'mymodule'
console.log(React === ReactFromModule) // true :)

针对另一条评论,仅将 React 移至 peerDependencies 并不能充分解决所有情况下的问题。我会直接回复那个评论,但 Whosebug 需要更多的声誉来回应错误的答案,而不是 post 他们。

我有一个使用 Webpack 构建的共享 React 组件模块,运行 遇到了同样的问题。我在下面的评论中概述了一个可能的修复方法,它需要修改 peerDependencies 并以类似于 mtkopone 共享的答案的方式使用 npm link。 https://github.com/facebook/react/issues/13991#issuecomment-841509933

我的解决方案有点老套,不建议长期使用。如果您正在使用 Webpack(您将此问题标记为),本文可能会详细介绍更永久的解决方案 (https://medium.com/codex/duplicate-copy-of-react-errors-when-using-npm-link-e5011de0995d)。我还没有尝试过,但作者似乎已经尝试了所有(不正确的)解决方案,并且在尝试构建共享组件库时也 运行 遇到了钩子问题。

那篇文章的作者正在尝试调试 Create-React-App 应用程序。虽然 CRA 在后台使用 webpack,但您无法直接访问 webpack.config,因此作者必须执行一些变通方法才能这样做。如果你不使用 CRA,而只是普通的 Webpack,那么你可以考虑使用 webpack.config 的 resolve.alias 部分来确保没有重复的 React 副本(参见:https://blog.maximeheckel.com/posts/duplicate-dependencies-npm-link/

package.json 中添加 reactreact-dom 作为 peerDependencies 对我不起作用。

我不得不在 webpack 配置文件中添加一个别名:

// webpack.config.js
resolve: {
  alias: {
    react: path.resolve('./node_modules/react'),
}

我试图使用 peerDependencies 并删除 devDependencies 但它失败了。

原来我在我正在处理的库的父文件夹之一中有一个 node_modules 文件夹,并且从那里加载了 React 的重复版本,而不是试图使用的工具React 库。

我没有编辑 devDependencies 来删除 React,而是编写了一个小脚本来从 node_modules 文件夹中删除 peerDependencies 中的所有内容。

npm view --json=true . peerDependencies | jq -r 'keys | .[] |  @text' | while read dep; do  rm -r ./node_modules/${dep} && echo Removed ${dep}; done