一个封装 UMD 规范 React 组件库时可能出现的 React Hooks 错误

1,728 阅读3分钟

关键词

  • umd
  • react
  • hooks
  • peerDependencies

前言

最近在弄基于阿里低代码引擎的低代码,需要使用到 umd 规范的 React 组件库,在对公司内部的组件库改造时挺顺利的,但后来在自己的组件库实践时却出现了以下错误:
image.png

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 https://fb.me/react-invalid-hook-call for tips about how to debug and fix this problem.

解决历程

锁定问题出处

由于有过调整内部组件库的经验,我拿来了之前测试时的最小MVP,如下:
以此确保仅仅只是我的组件库的打包文件有问题,经测试问题确实出在我的 blued.js 中。

<!DOCTYPE html>
<html>

<head>
  <meta charset="UTF-8" />
  <title>blued</title>
  <script src="./react.development.js"></script>
  <script src="./react-dom.development.js"></script>
  <script src="./babel.min.js"></script>
  <script src="./moment-with-locales.min.js"></script>
  <script src="./lodash.min.js"></script>
  <!-- ngfed -->
  <script src="../../dist/blued.js"></script>
</head>

<body>
  <div id="root"></div>
  <script type="text/babel">
    console.dir(blued);
    class App extends React.Component {
      render() {
        return <blued.Button type="primary" disabled>
          这是一个按钮
        </blued.Button>
      }
    }
    ReactDOM.render(<App />, document.getElementById("root"));
  </script>
</body>

</html>

看报错找思路

错误1:React 和 React DOM 版本不匹配

You might have mismatching versions of React and the renderer (such as React DOM)

版本都是 16.14.0,此项排除

错误2:打破了 Hook 的规则

You might be breaking the Rules of Hooks

看我的最小 MVP 中,我压根就没有用 hooks

错误3:重复的 React

You might have more than one copy of React in the same app

仔细回想,我们在最小 MVP 中已经预先引入的 react 和 react-dom,而作为 React UI 组件库,我们是没必要将 react 再次打进打包文件的(参考 antd),再看项目文件打包配置,并没有针对 react 进行 external 处理。(如果不清楚 external,那需要补充一下 webpack 知识啦)

external 是什么?
简单的来说,就是打包过程中我期望把某些依赖不打进最终的输出文件中。虽然 antd 也是这么做的,但是大家可能很少或从来没有使用 script 引入 antd 的方式使用过它,那我举另一个例子:bootstrap。
bootstrap 是依赖于 jquery 的,作为一个纯粹的组件库,肯定是不希望把依赖都一并算进来,

  • 一会导致打包大小暴涨
  • 二会导致与依赖强制捆绑,如 jquery 修复 bug 了,却只能通过更新 bootstrap 来更新 jquery 版本,

因此 bootstrap 做了 external 出来,这也就是为什么在 script 引入 bootstrap 之前需要先 script 引入 jquery。

那现在试试。

解决方法

  1. 既然是要对 react 进行 external 处理,那么就是要对打包工具做配置,组件库这里使用的是 fatherjs 做打包处理(虽然他是基于 rollup的),因此我们先看 fatherjs 的文档,有可能 fatherjs 对配置做了封装,比如 extraRollupPlugins,实在找不到相关内容后再去找 rollup),搜索关键词:external

参考链接:

  1. github.com/umijs/fathe…
  2. github.com/umijs/fathe…
  1. 根据以上的 参考链接2,好像找到了解决方法,改它。

image.png

延伸

如果你使用的 webpack 进行打包,那么就需要去 webpack 文档中查找解决方案,关键词同样是 external

是否解决


image.png

参考文章

你还可以在以下地址找到本文:

  1. 一文搞懂 peerDependencies