React组件库使用Material UI时webpack/rollup打包后报无效Hook错误
问题根因
这是典型的单应用内存在多份React实例导致的Hooks调用错误,触发条件和你描述完全匹配:
- 自研组件没有依赖第三方UI库,打包时没有带入额外的React引用,所以运行正常
- 你的组件库依赖的
@mui/material在自身的node_modules目录下找到了独立安装的React,MUI内部调用Hooks时用的是这份独立React,和主项目运行用的React不是同一个实例,因此抛出错误
解决步骤
- 调整组件库的依赖声明
打开组件库的package.json,把react、react-dom、所有@mui相关、@emotion相关依赖全部从dependencies字段移动到peerDependencies字段,同时在devDependencies中保留这些依赖的对应版本供本地开发使用。示例配置:
{ "peerDependencies": { "react": "^17.0.0 || ^18.0.0", "react-dom": "^17.0.0 || ^18.0.0", "@mui/material": "^5.0.0", "@emotion/react": "^11.0.0", "@emotion/styled": "^11.0.0", "@mui/lab": "^5.0.0-alpha.0" }, "devDependencies": { "react": "^18.2.0", "react-dom": "^18.2.0", "@mui/material": "^5.14.0", // 其余开发依赖保留不变 } }
这个配置会要求安装组件库的项目自行提供对应版本的React和MUI依赖,不会在组件库的node_modules下单独安装这些公共依赖。
- 补全Rollup打包的外部依赖配置
你当前的Rollup配置没有声明外部依赖,会把React和MUI的代码直接打包进组件库产物,导致多实例问题。修改rollup.config.js新增external字段:
import resolve from "@rollup/plugin-node-resolve"; import commonjs from "@rollup/plugin-commonjs"; import typescript from "@rollup/plugin-typescript"; import dts from "rollup-plugin-dts"; const pkg = require("./package.json"); const config = [ { input: "src/index.ts", // 新增下面这行,声明这些依赖不需要打包,直接复用宿主项目的依赖 external: ['react', 'react-dom', '@mui/material', '@emotion/react', '@emotion/styled', '@mui/lab'], output: [ { file: pkg.main, format: "cjs", sourcemap: true }, { file: pkg.module, format: "esm", sourcemap: true }, ], plugins: [ resolve(), commonjs(), typescript({ tsconfig: "./tsconfig.json" }), ], }, { input: "lib/esm/types/index.d.ts", output: [{ file: "lib/index.d.ts", format: "esm" }], plugins: [dts()], }, ]; export default config;
你的Webpack配置已经做了外部依赖声明,不需要修改。
- 清理缓存重新安装
- 删除组件库目录下的
node_modules、锁文件(package-lock.json/yarn.lock/pnpm-lock.yaml)和历史打包产物lib文件夹 - 重新安装组件库的开发依赖,执行打包命令生成最新的库产物
- 删除主项目目录下的
node_modules和锁文件 - 重新执行
npm i ./shared/path_to_library安装本地组件库
- 验证修复结果
安装完成后可以检查主项目node_modules/你的组件库名目录下是否存在react或者@mui文件夹,如果不存在说明配置生效。也可以分别在主项目入口和组件库的MUI封装组件中打印React实例,确认两者是同一个引用:
// 打印React实例,两个地方打印的引用完全一致则说明多实例问题已解决 import React from 'react' console.log(React)
内容的提问来源于stack exchange,提问作者Vinicius Souza
相关产品推荐
相关产品推荐

