将 MUI 组件添加到组件库会导致错误“只能在功能组件的主体内部调用挂钩”
Posted
技术标签:
【中文标题】将 MUI 组件添加到组件库会导致错误“只能在功能组件的主体内部调用挂钩”【英文标题】:Adding MUI Component to Component Library results in Error "Hooks can only be called inside of the body of a functional component" 【发布时间】:2021-08-22 00:55:00 【问题描述】:我有一个导出组件的组件库
// Test.tsx
import React from 'react';
const Test = () => <p>Test</p>;
export default Test;
和
// TestB.tsx
import React from 'react';
import Typography from '@material-ui/core';
const TestB = () => <Typography>TestB</Typography>;
export default TestB
在我的另一个包中,我正在导入组件
import React from 'react';
import Box from '@components';
import Test, TestB from 'component-library';
const Example = (): JSX.Element | null =>
return (
<Box mb=3>
<Test />
<TestB />
</Box>
);
;
export default Example;
当我导入 Test
时,我得到了预期的 <p>Test</p>
,但是当我导入 TestB
时,我得到了以下错误:
react.development.js:1476 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 https://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem.
at resolveDispatcher (react.development.js:1476)
at Object.useContext (react.development.js:1484)
at useTheme (useTheme.js:4)
at useStyles (makeStyles.js:222)
at WithStyles (withStyles.js:55)
at renderWithHooks (react-dom.development.js:14803)
at updateForwardRef (react-dom.development.js:16816)
at beginWork (react-dom.development.js:18645)
at htmlUnknownElement.callCallback (react-dom.development.js:188)
at Object.invokeGuardedCallbackDev (react-dom.development.js:237)
我已经确认react
和react-dom
都在16.9.0
版本上
-- 虽然这似乎无关紧要,因为它适用于Test
,但不适用于TestB
我不知道如何通过实现 TestB
来破坏 Rules of Hooks
,因为它不使用任何钩子
我已经尝试通过 package.json 删除除了一个版本之外的所有 react
-- 这似乎并不相关,因为它适用于Test
,但不适用于TestB
组件库 package.json 依赖项
"dependencies":
"@material-ui/core": "4.11.4",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-is": "^17.0.2",
"react-svg": "^13.0.6"
,
"devDependencies":
"@babel/core": "^7.14.3",
"@types/jest": "^24.0.24",
"@typescript-eslint/eslint-plugin": "^4.25.0",
"@typescript-eslint/parser": "^4.25.0",
"babel-loader": "^8.2.2",
"eslint": "^7.27.0",
"eslint-config-airbnb": "^18.2.1",
"eslint-plugin-import": "^2.23.3",
"eslint-plugin-jsx-a11y": "^6.4.1",
"eslint-plugin-react": "^7.23.2",
"eslint-plugin-react-hooks": "^4.2.0",
"jest": "^24.9.0",
"rollup": "^1.27.13",
"rollup-plugin-commonjs": "^10.1.0",
"rollup-plugin-node-resolve": "^5.2.0",
"rollup-plugin-peer-deps-external": "^2.2.0",
"rollup-plugin-typescript2": "^0.25.3",
"standard": "^14.3.1",
"standard-prettier": "^1.0.1",
"ts-jest": "^24.2.0",
"typescript": "^3.7.3"
,
项目 package.json 依赖项
"dependencies":
"@babel/core": "^7.1.6",
"@babel/plugin-proposal-class-properties": "^7.1.0",
"@babel/plugin-proposal-object-rest-spread": "^7.0.0",
"@babel/plugin-syntax-dynamic-import": "^7.2.0",
"@babel/plugin-transform-flow-strip-types": "^7.1.6",
"@babel/plugin-transform-modules-commonjs": "^7.4.3",
"@babel/polyfill": "^7.6.0",
"@babel/preset-env": "^7.9.0",
"@babel/preset-react": "^7.0.0",
"@babel/preset-typescript": "^7.9.0",
"@date-io/moment": "1.x",
"@emotion/core": "^10.0.10",
"@material-ui/core": "^4.11.4",
"@material-ui/icons": "^4.9.1",
"@material-ui/lab": "^4.0.0-alpha.56",
"@material-ui/pickers": "^3.2.10",
"@stripe/react-stripe-js": "^1.1.2",
"@stripe/stripe-js": "^1.4.0",
"@styled-system/css": "^1.0.3",
"@typeform/embed": "^0.5.12",
"attr-accept": "^1.1.0",
"autoprefixer": "^8.0.0",
"awesome-typescript-loader": "^4.0.0",
"axios": "^0.19.2",
"babel-loader": "^8.0.4",
"babel-plugin-dynamic-import-node": "^2.3.0",
"babel-plugin-styled-components": "^1.10.0",
"classnames": "^2.2.5",
"compression-webpack-plugin": "^1.0.0",
"core-js": "^3.2.1",
"cropperjs": "^1.3.3",
"css-hot-loader": "^1.3.6",
"css-loader": "^0.28.7",
"d3": "^6.2.0",
"d3-selection": "^2.0.0",
"date-fns": "^2.9.0",
"dot-prop-immutable": "^1.4.0",
"downshift": "^5.0.3",
"emotion-theming": "^10.0.10",
"empty": "^0.10.1",
"enzyme": "^3.6.0",
"enzyme-adapter-react-16": "^1.5.0",
"enzyme-to-json": "^3.3.4",
"eslint": "^7.3.1",
"eslint-config-airbnb": "^16.1.0",
"eslint-plugin-flowtype": "^2.46.1",
"eslint-plugin-import": "^2.8.0",
"eslint-plugin-jest": "^21.5.0",
"eslint-plugin-jsx-a11y": "^6.0.2",
"eslint-plugin-react": "^7.5.1",
"extract-text-webpack-plugin": "^3.0.1",
"faker": "^4.1.0",
"fetch-mock": "^6.5.2",
"file-loader": "^0.11.2",
"file-saver": "^2.0.2",
"final-form": "^4.20.1",
"final-form-arrays": "^3.0.2",
"final-form-calculate": "^1.3.1",
"glob": "^7.1.2",
"hellosign-embedded": "^2.8.0",
"identity-obj-proxy": "^3.0.0",
"isomorphic-fetch": "^2.2.1",
"js-yaml": "^3.10.0",
"jwt-decode": "^2.2.0",
"lint-staged": "^10.2.11",
"loaders.css": "^0.1.2",
"lost": "^8.2.0",
"components": "https://github.com/example/components#dev",
"mock-geolocation": "^1.0.11",
"moment": "2.24.0",
"moment-locales-webpack-plugin": "^1.2.0",
"moment-timezone": "^0.5.28",
"node-sass": "^4.12.0",
"notistack": "^0.9.11",
"numeral": "^2.0.6",
"path-complete-extname": "^1.0.0",
"postcss-automath": "^1.0.1",
"postcss-calc": "^7.0.2",
"postcss-flexbugs-fixes": "^3.2.0",
"postcss-font-magician": "^2.0.0",
"postcss-import": "^10.0.0",
"postcss-initial": "^2.0.0",
"postcss-loader": "^2.0.6",
"postcss-smart-import": "^0.7.5",
"precss": "^2.0.0",
"prettier-eslint": "^8.7.0",
"prop-types": "^15.5.10",
"qs": "^6.5.1",
"ramda": "^0.27.1",
"rc-time-picker": "^3.7.2",
"react": "17.0.2",
"react-addons-shallow-compare": "^15.6.0",
"react-confetti": "^6.0.0",
"react-content-loader": "^4.0.1",
"react-cropper": "^1.0.1",
"react-dates": "^21.8.0",
"react-datetime": "^2.16.3",
"react-dom": "17.0.2",
"react-final-form": "^6.5.1",
"react-final-form-arrays": "^3.1.2",
"react-final-form-listeners": "^1.0.3",
"react-hot-loader": "^4.6.3",
"react-measure": "^2.0.2",
"react-modal": "^3.8.1",
"react-motion": "^0.5.1",
"react-onclickoutside": "^6.7.0",
"react-portal": "^4.0.0",
"react-pose": "^4.0.8",
"react-pose-text": "^3.1.0",
"react-query": "^2.5.13",
"react-redux": "^7.2.0",
"react-resize-detector": "^6.6.0",
"react-router": "^5.1.2",
"react-router-dom": "^5.1.2",
"react-select": "^1.0.0-rc.10",
"react-svg-loader": "^1.1.1",
"react-switch": "^2.0.0",
"react-table": "^7.0.1",
"react-tabs": "^2.2.2",
"react-test-renderer": "^16.5.2",
"react-text-mask": "^5.4.3",
"react-textfit": "^1.1.0",
"react-use": "^15.3.4",
"react-virtualized-auto-sizer": "^1.0.2",
"react-window": "^1.8.6",
"react-window-infinite-loader": "^1.0.5",
"react-youtube": "^7.5.0",
"react_ujs": "^2.4.4",
"recompose": "^0.26.0",
"redux": "^4.0.1",
"redux-actions": "^2.6.5",
"redux-analytics": "^0.3.1",
"redux-form": "^8.3.6",
"redux-mock-store": "^1.4.0",
"redux-observable": "^1.1.0",
"redux-persist": "^5.6.12",
"redux-responsive": "^4.3.8",
"redux-thunk": "^2.2.0",
"regenerator-runtime": "^0.11.0",
"reselect": "^3.0.1",
"resolve-url-loader": "^2.1.0",
"rxjs": "^6.4.0",
"sass-loader": "^6.0.6",
"shortid": "^2.2.8",
"sinon": "^6.3.5",
"start-server-and-test": "^1.7.11",
"style-loader": "^0.18.2",
"styled-components": "^4.1.3",
"styled-normalize": "^8.0.4",
"styled-system": "^3.2.1",
"svg-react-loader": "^0.4.5",
"swr": "^0.2.0",
"ts-jest": "^23.1.3",
"typescript": "^3.8.3",
"typings-for-css-modules-loader": "^1.7.0",
"warnings-to-errors-webpack-plugin": "^2.0.1",
"webpack": "^3.12.0",
"webpack-bundle-analyzer": "^3.0.3",
"webpack-dev-server": "^2.7.1",
"webpack-manifest-plugin": "^1.3.1",
"webpack-merge": "^4.1.0",
"whatwg-fetch": "^3.0.0"
,
"devDependencies":
"@svgr/cli": "^5.4.0",
"@testing-library/cypress": "^6.0.0",
"@testing-library/react": "^8.0.6",
"@types/classnames": "^2.2.3",
"@types/faker": "^4.1.4",
"@types/fetch-mock": "^6.0.3",
"@types/intercom-web": "^2.8.7",
"@types/jest": "^25.2.3",
"@types/jwt-decode": "^2.2.1",
"@types/moment-timezone": "^0.5.4",
"@types/qs": "^6.5.1",
"@types/query-string": "^6.3.0",
"@types/ramda": "^0.27.36",
"@types/react": "^16.7.20",
"@types/react-content-loader": "^3.1.4",
"@types/react-dates": "^17.1.14",
"@types/react-dom": "^16.0.11",
"@types/react-loadable": "^5.4.2",
"@types/react-measure": "^2.0.2",
"@types/react-modal": "^3.8.2",
"@types/react-motion": "^0.0.26",
"@types/react-onclickoutside": "^6.0.3",
"@types/react-redux": "^7.1.9",
"@types/react-router-dom": "^5.1.3",
"@types/react-select": "^1.2.7",
"@types/react-table": "^7.0.13",
"@types/react-test-renderer": "^16.0.2",
"@types/recompose": "^0.26.1",
"@types/redux-actions": "^2.6.0",
"@types/redux-form": "^7.4.16",
"@types/redux-mock-store": "^1.0.0",
"@types/shortid": "^0.0.29",
"@types/sinon": "^5.0.5",
"@types/styled-components": "^4.1.18",
"@types/styled-system": "^3.1.1",
"@types/webpack-env": "^1.13.6",
"@typescript-eslint/eslint-plugin": "^3.4.0",
"@typescript-eslint/parser": "^3.4.0",
"cypress": "6.1.0",
"eslint-config-prettier": "^6.11.0",
"eslint-plugin-prettier": "^3.1.4",
"jest": "^26.0.1",
"miragejs": "^0.1.35",
"pre-commit": "^1.2.2",
"prettier": "^2.0.5"
,
有什么想法吗?
【问题讨论】:
向我们展示您如何导入和使用TextB
@Dominik 更新
能否在return语句前console.log(TestB)确保它是一个组件/函数?
@JordanTheGenius $$typeof: Symbol(react.element), type: ..., key: null, ref: null, props: ..., ... $$typeof: Symbol( react.element) key: null props: children: "-Tested-" ref: null type: $$typeof: Symbol(react.forward_ref), propTypes: …, Naked: …, options: ...,渲染:ƒ,... _owner:null _store:已验证:false _self:null _source:null proto:Object
版本相关,Typography 内部使用了钩子,这就是为什么您在 TestB 中遇到错误的原因,请尝试在项目中设置与组件包中相同的 react 版本
【参考方案1】:
将大部分内容移至 devDependencies 并添加准备脚本似乎是我所缺少的。
我的 package.json 最终看起来像这样
"version": "0.1.0",
"main": "dist/index.js",
"module": "build/index.es.js",
"jsnext:main": "build/index.es.js",
"files": ["dist", "build"],
"scripts":
"prepare": "npm run build",
"build": "rollup -c",
"format": "prettier-standard --format",
"test": "jest --coverage",
"storybook": "start-storybook -p 6006",
"build-storybook": "build-storybook",
"lint": "eslint 'src/**/*.j,ts,j,tsx'",
,
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": ,
"peerDependencies":
"@material-ui/core": "4.11.4",
"react": "^16"
,
"devDependencies":
"@babel/core": "^7.14.3",
"@material-ui/core": "4.11.4",
"@storybook/addon-actions": "^6.2.9",
"@storybook/addon-essentials": "^6.2.9",
"@storybook/addon-knobs": "^6.2.9",
"@storybook/addon-links": "^6.2.9",
"@storybook/react": "^6.2.9",
"@types/jest": "^24.0.24",
"@typescript-eslint/eslint-plugin": "^4.25.0",
"@typescript-eslint/parser": "^4.25.0",
"babel-loader": "^8.2.2",
"eslint-config-airbnb": "^18.2.1",
"eslint-plugin-import": "^2.23.3",
"eslint-plugin-jsx-a11y": "^6.4.1",
"eslint-plugin-react-hooks": "^4.2.0",
"eslint-plugin-react": "^7.23.2",
"eslint": "^7.27.0",
"jest": "^24.9.0",
"react-dom": "^17.0.2",
"react-is": "^17.0.2",
"react-svg": "^13.0.6",
"react": "^17.0.2",
"rollup-plugin-commonjs": "^10.1.0",
"rollup-plugin-node-resolve": "^5.2.0",
"rollup-plugin-peer-deps-external": "^2.2.0",
"rollup-plugin-typescript2": "^0.25.3",
"rollup": "^1.27.13",
"standard-prettier": "^1.0.1",
"standard": "^14.3.1",
"storybook-addon-paddings": "^4.1.1",
"storybook-dark-mode": "^1.0.8",
"ts-jest": "^24.2.0",
"typescript": "^3.7.3"
,
"jest":
"preset": "ts-jest",
"testEnvironment": "node"
,
"standard":
"ignore": [
"node_modules/",
"build/"
]
并按要求提供 rollup.config
import typescript from 'rollup-plugin-typescript2'
import commonjs from 'rollup-plugin-commonjs'
import external from 'rollup-plugin-peer-deps-external'
import resolve from 'rollup-plugin-node-resolve'
import pkg from './package.json'
export default
input: 'src/index.ts',
output: [
file: pkg.main,
format: 'cjs',
exports: 'named',
sourcemap: true
,
file: pkg.module,
format: 'es',
exports: 'named',
sourcemap: true
],
external: ["react", "@material-ui/core", "react-is"],
plugins: [
external(),
resolve(),
typescript(
rollupCommonJSResolveHack: true,
exclude: [
'**/__tests__/**',
'**/*.stories.*'
],
clean: true
),
commonjs(
include: ['node_modules/**'],
namedExports:
)
]
【讨论】:
您需要 material-ui 并作为 peerDependencies 做出反应的任何原因?那是错误的试验吗? 你能分享你的汇总配置吗?你对@rollup/plugin-node-resolve
或rollup-plugin-peer-deps-external
做了什么吗?
我的理解是对等依赖调用它可以期望主机包已经拥有该包。这是一篇文章nodejs.org/es/blog/npm/peer-dependencies以上是关于将 MUI 组件添加到组件库会导致错误“只能在功能组件的主体内部调用挂钩”的主要内容,如果未能解决你的问题,请参考以下文章
样式化的 MUI 无法在 typescript 中将组件道具传递给 Typography