【问题标题】:React package, invalid hook call反应包,无效的钩子调用
【发布时间】:2021-08-17 20:55:42
【问题描述】:

我正在尝试编写一个包含一些 React 内容的 NPM 包,目前它只是一个组件和一个钩子。要构建我正在使用 Webpack 的包。我已将reactreact-dom 添加到外部部分,以确保它不包含在捆绑包中。我还在package.json 中将react 标记为peerDependency,并将其包含为devDependency。尝试在另一个项目中使用捆绑包时,我仍然收到错误Invalid hook call。我想我已经尝试了所有可以通过 Google 搜索到的方法(例如使用 package 来解决这个问题),但没有成功。

我的 Webpack 配置现在看起来像这样:

const path = require('path');

const isProduction = process.env.NODE_ENV?.toLowerCase() === 'production';

const config = {
  entry: './src/index.ts',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'index.js',
    libraryTarget: 'umd',
  },
  plugins: [],
  module: {
    rules: [
      {
        test: /\.(tsx?)$/i,
        loader: 'ts-loader',
        exclude: ['/node_modules/'],
      },
      {
        test: /\.(graphql|gql)$/,
        exclude: /node_modules/,
        loader: 'graphql-tag/loader',
      },
    ],
  },
  resolve: {
    extensions: ['.ts', '.tsx', '.js', '.graphql', '.json'],
  },
  externals: {
    react: 'react',
    'react-dom': 'react-dom',
  },
};

module.exports = () => {
  if (isProduction) {
    config.mode = 'production';
  } else {
    config.mode = 'development';
  }
  return config;
};

package.json 的要点如下:

{
  ...
  "main": "./dist/index.js",
  "types": "./dist/index.d.ts",
  ...
  "scripts": {
    ...
    "build": "webpack --mode=production --node-env=production",
    "build:dev": "webpack --mode=development",
    ...
    "prepublishOnly": "npm run build"
  },
  ...
  "peerDependencies": {
    "react": "16.8.0 || ^17.0.2"
  },
  "peerDependenciesMeta": {
    "react": {
      "optional": true
    }
  },
  "dependencies": {
    "@apollo/client": "^3.3.19",
    "apollo-server-errors": "^2.5.0",
    "joi": "^17.4.0",
    "lodash": "^4.17.21"
  },
  "devDependencies": {
    ...
    "react": "^17.0.2",
    "react-dom": "^17.0.2",
    ...
  }
}

钩子非常简单,它只是尝试使用useContext 由组件创建的上下文,以确保此逻辑没有问题我尝试仅使用setState 与相同结果。钩子看起来有点像这样:

function useClient(): Client {
  return useContext(getContext());
}

getContext 只是一个创建或重用现有React.Context 的函数(深受 Apollo Client 的启发):

const cache = new (canUseWeakMap ? WeakMap : Map)<
  typeof createContext,
  Context<Client | undefined>
>();

function getContext() {
  let context = cache.get(createContext);

  if (!context) {
    context = createContext<Client | undefined>(undefined);
    context.displayName = 'ClientContext';
    cache.set(createContext, context);
  }

  return context;
}

export default getContext;

我尝试使用钩子的组件是一个简单的功能组件:

const HelloWorld: FC<HelloWorldProps> = () => {
  const client = useClient();

  return (
    <div>Hello World!</div>
  );
};

我错过了什么?非常感谢您的帮助!


编辑:

我在一个仅包含基础知识的小型示例应用程序中重现了该问题,使用setState 的外部包,然后在create-react-app 中使用该包,结果相同:

https://github.com/ganhammar/invalid-hook-call

【问题讨论】:

  • 您是否在本地使用符号链接进行尝试?
  • 使用development 模式,然后检查webpack 构建的输出(通常在dist 文件夹中)。您能否验证该文件的内容是否正确地捆绑了您的组件(并外部化了react)?
  • 这很奇怪。作为exported 函数useExternal 不应将其名称弄乱。函数o 不是一个有效的钩子,因为它没有use 名称前缀。此外,在development 模式下,Webpack 根本不应该破坏名称。双重奇怪。确保 (i) 它实际上是 mode: development。在 production 模式下 (ii) 通过手动提供 TerserPluginkeep_fnames: true 来禁用名称修改 - 请参阅 webpack.js.org/plugins/terser-webpack-plugin/#terseroptions 。此外,请确保 (iii) 您的函数实际上已导出到输出文件中。
  • 显然,webpack v5 的一些默认设置存在问题。我之前遇到过类似的问题。这里有一个很长的讨论:github.com/webpack/webpack/issues/11277#issuecomment-723476599。尝试设置devtool: 'source-map'。如果没有其他办法,降级到webpack@4,那肯定行。
  • 另外,尝试将const useExternal = () =&gt; useState('external package'); 重写为function useExternal() { return useState('external package'); }。这可能会修复 production 中的名称错误。 Function.nameArrowFunctionExpression 正常工作是一个相当新的概念。不确定webpack 是否正确。

标签: reactjs typescript webpack react-hooks


【解决方案1】:

感谢大家的帮助!

问题是我停止发布包,而是使用file:../Client 在本地安装了依赖项。这导致了React 的重复实例,因为它使用了React 的本地到客户端包实例。仅发布构建的输出,然后安装该依赖项为我解决了这个问题。

如果其他人偶然发现,我发现以下 answer 有助于我实现这一点(链接两个包之间的 react 依赖项)。

【讨论】:

    猜你喜欢
    • 2020-12-02
    • 1970-01-01
    • 1970-01-01
    • 2019-08-17
    • 2019-09-25
    • 1970-01-01
    • 2019-11-29
    • 2020-11-29
    • 1970-01-01
    相关资源
    最近更新 更多