React组件库出现Cannot read properties of null (reading 'useState')错误的解决
问题
我正在开发一个信用卡表单的React组件库,相关代码及配置如下:
CreditCardForm.tsx
import React, {useState} from "react"; interface CreditCardFormProps { testState: any; name: string; onChangeName: () => void; cardNumber: string; onChangeCardNumber: () => void; expirationDate: string; onChangeExpirationDate: () => void; cvv: string; onChangeCvv: () => void; onSubmit: () => void; } const CreditCardForm = (props: CreditCardFormProps) => { const [testState, setTestState] = useState<any>("hello") return ( <form onSubmit={props.onSubmit}> <label htmlFor="name">Name on Card</label> <input type="text" id="name" name="name" value={props.name} onChange={props.onChangeName} required pattern="^[A-Za-z\s]*$" title="Please input your name correctly" /> <p>test: {testState}</p> <label htmlFor="cardNumber">Card Number</label> <input type="text" id="cardNumber" name="cardNumber" value={props.cardNumber} onChange={props.onChangeCardNumber} required minLength={16} maxLength={16} pattern="^[0-9]*$" title="Your Card Number should contain only numbers." /> <label htmlFor="expirationDate">Expiration Date</label> <input type="number" id="expirationDate" name="expirationDate" value={props.expirationDate} onChange={props.onChangeExpirationDate} required title="Please input your Expiration Date correctly" /> <label htmlFor="cvv">CVV</label> <input type="text" id="cvv" name="cvv" value={props.cvv} onChange={props.onChangeCvv} required minLength={3} maxLength={3} title="Your CVV should contain only numbers." /> <button type="submit">Submit</button> </form> ); }; export default CreditCardForm;
package.json
{ "name": "@packagename", "version": "0.1.24", "description": "A react component library", "scripts": { "rollup": "rollup -c" }, "author": " Name ", "license": "ISC", "peerDependencies": { "react-dom": "^18.2.0", "react": "^18.0.2" }, "devDependencies": { "@rollup/plugin-commonjs": "^24.1.0", "@rollup/plugin-node-resolve": "^15.0.2", "@rollup/plugin-typescript": "^11.1.0", "@types/react": "^18.2.0", "rollup": "^3.21.0", "rollup-plugin-dts": "^5.3.0", "source-map-explorer": "^2.5.3", "tslib": "^2.5.0", "typescript": "^5.0.4" }, "main": "dist/cjs/index.js", "module": "dist/esm/index.js", "files": [ "dist" ], "types": "dist/index.d.ts", "publishConfig": { "registry": "https://npm.pkg.github.com/@username" }, "dependencies": { "react": "^18.0.2", "react-dom": "^18.2.0" } }
rollup.config.mjs
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"; import packageJson from "../package.json" assert { type: "json" }; //we import package.json so when we use commonjs modules export default [ //first configuration object { input: "src/index.ts", //entry point for this part of our library (it exports all of our components so that the user can import the library) output: [ { file: packageJson.main, format: "cjs", sourcemap: true, }, //commonjs modules, which will use the main field of packageJson module { file: packageJson.module, format: "esm", sourcemap: true, }, //for the es6 modules ], plugins: [ resolve(), commonjs(), typescript({ tsconfig: "../tsconfig.json" }), ], //node resolve plugin, and the other plugins (typescript plugin needs the specific directory) }, //second configuration object { input: "dist/esm/types/index.d.ts", output: [{ file: "dist/index.d.ts", format: "esm" }], plugins: [dts()], }, ];
发布该库并在测试React项目中安装运行后,出现如下错误:
Compiled with problems: × ERROR Cannot read properties of null (reading 'useState') TypeError: Cannot read properties of null (reading 'useState') at Object.useState (http://localhost:3000/static/js/bundle.js:2135:29) at CreditCardForm (http://localhost:3000/static/js/bundle.js:3112:50) at renderWithHooks (http://localhost:3000/static/js/bundle.js:24102:22) at mountIndeterminateComponent (http://localhost:3000/static/js/bundle.js:27388:17) at beginWork (http://localhost:3000/static/js/bundle.js:28684:20) at HTMLUnknownElement.callCallback (http://localhost:3000/static/js/bundle.js:13694:18) at Object.invokeGuardedCallbackDev (http://localhost:3000/static/js/bundle.js:13738:20) at invokeGuardedCallback (http://localhost:3000/static/js/bundle.js:13795:35) at beginWork$1 (http://localhost:3000/static/js/bundle.js:33669:11) at performUnitOfWork (http://localhost:3000/static/js/bundle.js:32916:16)
原因分析
这个错误核心是组件库打包时把React和ReactDOM一起打包进了产物,导致测试项目中同时存在两份React实例:一份是项目自身依赖的React,另一份是组件库打包带进来的React。当组件调用useState时,实际引用的是组件库打包的那份未与项目React上下文绑定的实例,最终导致React对象为null,无法读取useState方法。
具体诱因:
package.json中把React和ReactDOM放在了dependencies中,Rollup默认会将这类依赖打包进最终产物。- 未配置Rollup的
external选项,没有明确告知Rollup不要打包React这类peer依赖。
解决方法
步骤1:调整package.json依赖配置
把dependencies中的React和ReactDOM删除,保留peerDependencies中的配置即可,确保组件库不会安装自己的React副本,而是直接使用宿主项目的React:
修改后的依赖部分:
"peerDependencies": { "react-dom": "^18.2.0", "react": "^18.0.2" }, "devDependencies": { "@rollup/plugin-commonjs": "^24.1.0", "@rollup/plugin-node-resolve": "^15.0.2", "@rollup/plugin-typescript": "^11.1.0", "@types/react": "^18.2.0", "rollup": "^3.21.0", "rollup-plugin-dts": "^5.3.0", "source-map-explorer": "^2.5.3", "tslib": "^2.5.0", "typescript": "^5.0.4" }
步骤2:配置Rollup的external选项
在Rollup配置的第一个打包对象中添加external字段,指定React和ReactDOM为外部依赖,禁止Rollup将它们打包进产物:
修改后的rollup.config.mjs第一部分:
{ input: "src/index.ts", output: [ { file: packageJson.main, format: "cjs", sourcemap: true, }, { file: packageJson.module, format: "esm", sourcemap: true, }, ], external: ["react", "react-dom"], // 添加此配置 plugins: [ resolve(), commonjs(), typescript({ tsconfig: "../tsconfig.json" }), ], },
步骤3:重新打包发布
执行npm run rollup重新打包组件库,然后重新发布到私有仓库。
步骤4:更新测试项目的组件库
在测试项目中重新安装组件库,确保使用修改后的版本,此时组件会正确复用项目自身的React实例,错误即可解决。
内容的提问来源于stack exchange,提问作者bytebunny
相关产品推荐
相关产品推荐

