为什么我收到此错误:无法读取 React 包上的 null 属性(读取“useState”)?

Mar*_*na 5 reactjs react-hooks

我正在为信用卡表单创建一个 React 组件库,CreditCardForm.tsx 如下所示:

\n

CreditCardForm.tsx:

\n
import React, {useState} from "react";\n\ninterface CreditCardFormProps {\n  testState: any;\n  name: string;\n  onChangeName: () => void;\n  cardNumber: string;\n  onChangeCardNumber: () => void;\n  expirationDate: string;\n  onChangeExpirationDate: () => void;\n  cvv: string;\n  onChangeCvv: () => void;\n  onSubmit: () => void;\n}\n\n\nconst CreditCardForm = (props: CreditCardFormProps) => {\n  const [testState, setTestState] =useState<any>("hello")\n  return (\n    <form onSubmit={props.onSubmit}>\n      <label htmlFor="name">Name on Card</label>\n      <input\n        type="text"\n        id="name"\n        name="name"\n        value={props.name}\n        onChange={props.onChangeName}\n        required\n        pattern="^[A-Za-z\\s]*$"\n        title="Please input your name correctly"\n      />\n      <p>test: {testState}</p>\n\n      <label htmlFor="cardNumber">Card Number</label>\n      <input\n        type="text"\n        id="cardNumber"\n        name="cardNumber"\n        value={props.cardNumber}\n        onChange={props.onChangeCardNumber}\n        required\n        minLength={16}\n        maxLength={16}\n        pattern="^[0-9]*$"\n        title="Your Card Number should contain only numbers."\n      />\n        <label htmlFor="expirationDate">Expiration Date</label>\n        <input\n          type="number"\n          id="expirationDate"\n          name="expirationDate"\n          value={props.expirationDate}\n          onChange={props.onChangeExpirationDate}\n          required\n          title="Please input your Expiration Date correctly"\n\n        />\n\n      <label htmlFor="cvv">CVV</label>\n      <input\n        type="text"\n        id="cvv"\n        name="cvv"\n        value={props.cvv}\n        onChange={props.onChangeCvv}\n        required\n        minLength={3}\n        maxLength={3}\n        title="Your CVV should contain only numbers."\n\n      />\n\n      <button type="submit">Submit</button>\n    </form>\n  );\n};\n\nexport default CreditCardForm;\n
Run Code Online (Sandbox Code Playgroud)\n

包.json

\n
{\n  "name": "@packagename",\n  "version": "0.1.24",\n  "description": "A react component library",\n  "scripts": {\n    "rollup": "rollup -c"\n  },\n  "author": " Name ",\n  "license": "ISC",\n  "peerDependencies": {\n    "react-dom": "^18.2.0",\n    "react": "^18.0.2"\n  },\n  "devDependencies": {\n    "@rollup/plugin-commonjs": "^24.1.0",\n    "@rollup/plugin-node-resolve": "^15.0.2",\n    "@rollup/plugin-typescript": "^11.1.0",\n    "@types/react": "^18.2.0",\n    "rollup": "^3.21.0",\n    "rollup-plugin-dts": "^5.3.0",\n    "source-map-explorer": "^2.5.3",\n    "tslib": "^2.5.0",\n    "typescript": "^5.0.4"\n  },\n  "main": "dist/cjs/index.js",\n  "module": "dist/esm/index.js",\n  "files": [\n    "dist"\n  ],\n  "types": "dist/index.d.ts",\n  "publishConfig": {\n    "registry": "https://npm.pkg.github.com/@username"\n  },\n  "dependencies": {\n    "react": "^18.0.2",\n    "react-dom": "^18.2.0"\n\n  }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

我使用 rollup 来构建我的包,我的 rollup.config.mjs 文件如下所示:

\n
import resolve from "@rollup/plugin-node-resolve";\nimport commonjs from "@rollup/plugin-commonjs";\nimport typescript from "@rollup/plugin-typescript";\nimport dts from "rollup-plugin-dts";\nimport packageJson from "./package.json" assert { type: "json" }; //we import package.json so when we use commonjs modules\n\nexport default [\n  //first configuration object\n  {\n    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)\n    output: [\n      {\n        file: packageJson.main,\n        format: "cjs",\n        sourcemap: true,\n      }, //commonjs modules, which will use the main field of packageJson module\n      {\n        file: packageJson.module,\n        format: "esm",\n        sourcemap: true,\n      }, //for the es6 modules\n    ],\n    plugins: [\n      resolve(),\n      commonjs(),\n      typescript({ tsconfig: "./tsconfig.json" }),\n    ], //node resolve plugin, and the other plugins (typescript plugin needs the specific directory)\n  },\n\n  //second configuration object\n  {\n    input: "dist/esm/types/index.d.ts",\n    output: [{ file: "dist/index.d.ts", format: "esm" }],\n    plugins: [dts()],\n  },\n];\n\n
Run Code Online (Sandbox Code Playgroud)\n

因此,当我发布这个反应库(npmpublish),并将其安装在反应应用程序项目上(以测试它)并运行该项目(npmstart)时,它会返回以下错误:

\n
Compiled with problems:\n\xc3\x97\nERROR\nCannot read properties of null (reading 'useState')\nTypeError: Cannot read properties of null (reading 'useState')\n    at Object.useState (http://localhost:3000/static/js/bundle.js:2135:29)\n    at CreditCardForm (http://localhost:3000/static/js/bundle.js:3112:50)\n    at renderWithHooks (http://localhost:3000/static/js/bundle.js:24102:22)\n    at mountIndeterminateComponent (http://localhost:3000/static/js/bundle.js:27388:17)\n    at beginWork (http://localhost:3000/static/js/bundle.js:28684:20)\n    at HTMLUnknownElement.callCallback (http://localhost:3000/static/js/bundle.js:13694:18)\n    at Object.invokeGuardedCallbackDev (http://localhost:3000/static/js/bundle.js:13738:20)\n    at invokeGuardedCallback (http://localhost:3000/static/js/bundle.js:13795:35)\n    at beginWork$1 (http://localhost:3000/static/js/bundle.js:33669:11)\n    at performUnitOfWork (http://localhost:3000/static/js/bundle.js:32916:16)\n
Run Code Online (Sandbox Code Playgroud)\n

为什么会出现这个问题以及如何修复它?

\n

Dan*_*cák 13

正如我们在评论中讨论的,当您构建一个应该使用主机运行时的库时,在您的情况react下 和react-dom,您必须将它们外部化。

基本上你应该做两件事:

  1. 包管理器知道您不希望您的库的客户端再次下载 React。在你的 lib 的 package.json 中,你必须将它们删除并react放入and中。表明您在本地使用 React 进行开发,并表明对于生产,React 将由主机应用程序提供。react-domdependenciesdevDependenciespeerDependenciesdevDependenciespeerDependencies
  2. 但这还不够,您还必须明确告诉Rollup它应该外部化 React deps。原因很简单 - 当 Rollup 浏览源文件时,它会解析import它找到的语句,并且主要通过查看node_modules. 在你的情况下,你是否定义了react并不重要dependenciesdevDependecies它只是找到react depsnode_modules并将它们捆绑在你的lib中。

您违反了第二个条件,并且最终在应用程序中出现了2 组 React 依赖项,这会引发错误。

现在你的错误很令人困惑,因为听起来反应导入被错误地解决了,但实际上最初的错误是You might have more than one copy of React in the same app(我尝试自己运行你的库)。

根本原因就这么多,现在如何解决第 2 点?只需配置 Rollup 来外部化 React 即可!

rollup.config.mjs

import external from 'rollup-plugin-peer-deps-external';

export default [
  {
    plugins: [
      external(),
      // your other plugins
    ],
    external: ["react", "react-dom"],
    // rest of your config
  }
];
Run Code Online (Sandbox Code Playgroud)

通过这种方式,您可以告诉 Rollup 它不应该捆绑reactreact-dom在遇到导入时进行捆绑,而是准备让主机应用程序拥有它。这将从您的 lib 包中删除 React deps 并消除您的错误!