You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用外部NextJS组件库时因多React实例引发Hook调用错误

多React实例导致Next.js组件Hook调用错误的解决方案

问题描述

构建存储通用组件的外部库时,使用带Hook的Next.js组件(如NextLink依赖useRef、NextImage依赖useContext)会触发错误:TypeError: Cannot read properties of null (reading 'useRef')。排查确认核心原因是库与宿主项目存在两个独立的React实例,推测问题出在基于Yarn的库构建与依赖管理环节,寻求解决冲突的方案。

组件库配置

库的package.json已将React放入peerDependencies避免重复引入:

{
  "name": "my-lib",
  "version": "1.0.0",
  "engines": {
    "yarn": "^1.22.19"
  },
  "packageManager": "yarn@1.22.19",
  "scripts": {
    "dist:cjs": "tsc --project tsconfig-cjs.json",
    "dist:esm": "tsc --project tsconfig.json",
    "dist": "yarn run dist:cjs & yarn run dist:esm"
  },
  "license": "ISC",
  "main": "./dist/cjs/index.js",
  "module": "./dist/esm/index.js",
  "peerDependencies": {
    "@chakra-ui/react": "*",
    "react": "*"
  },
  "devDependencies": {
    "@chakra-ui/react": "*",
    "@types/node": "^16.11.12",
    "@types/react": "^18.0.14",
    "@types/react-dom": "^18.0.5",
    "next": "^12.0.7",
    "react": "*",
    "typescript": "^4.4.4"
  },
  "dependencies": {
    "@chakra-ui/icons": "2.0.2",
    "@chakra-ui/theme-tools": "^2.0.2"
  }
}

使用tsc结合tsconfig.json编译库:

{
  "compilerOptions": {
    "declaration": true,
    "declarationMap": true,
    "module": "ES2020",
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "isolatedModules": true,

    "rootDir": "./src",
    "outDir": "./dist/esm",
    "target": "es2020",
    "sourceMap": true,
    "allowJs": true,
    "jsx": "react",

    "alwaysStrict": true,
    "noImplicitAny": true,
    "noImplicitThis": true,
    "strict": true,
    "strictBindCallApply": true,
    "strictFunctionTypes": true,
    "strictNullChecks": true,
    "strictPropertyInitialization": true,

    "esModuleInterop": true,

    "noFallthroughCasesInSwitch": true,
    "noImplicitReturns": true,
    "noPropertyAccessFromIndexSignature": false,
    "noUncheckedIndexedAccess": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,

    "allowUnreachableCode": false,
    "allowUnusedLabels": false,
    "forceConsistentCasingInFileNames": true,
    "newLine": "lf",
    "skipLibCheck": true,

    "preserveWatchOutput": true
  },
  "compileOnSave": true,
  "exclude": ["node_modules", "dist"]
}

组件示例

  • 可正常运行的自定义Hook组件:
import { useRef } from 'react'
import React from 'react'

export function TestHook() :JSX.Element {
  const count = useRef(0);
  return (
    <>
      Hello {JSON.stringify(count)}
    </>
  );
}
  • 无法运行的Next.js组件(引入NextLink):
import NextLink, { LinkProps as NextLinkProps } from 'next/link'
import React from 'react'

export function NavLink({ href, ...props }: Pick<NextLinkProps, 'href'>) {
  return (
    <NextLink {...{ href }} passHref>
      <a {...props} />
    </NextLink>
  )
}

项目配置

通过Git子模块将库链接到项目路径./modules/Lib,并通过file://方式安装:
项目package.json依赖配置:

"dependencies": {
    "my-lib": "file:modules/Lib",
    "next": "12.2.5",
    "react": "^18.2.0",
    "react-dom": "^18.2.0"
  },
  "devDependencies": {
    "typescript": "4.8.2"
  }

错误复现

  • 正常运行场景:使用库中TestHook组件或项目本地NavLink组件无异常:
import React from 'react'

import { TestHook } from 'my-lib'
import { NavLink } from './NavLink'

export function Home() :JSX.Element {
  return (
    <>
     <TestHook />
     <NavLink href={"/"}>Home</NavLink>
    </>
  );
}
  • 错误触发场景:使用库中NavLink组件时抛出错误:
import React from 'react'

import { TestHook, NavLink } from 'my-lib'

export function Home() :JSX.Element {
  return (
    <>
     <TestHook />
     <NavLink href={"/"}>Home</NavLink>
    </>
  );
}

错误信息:

Warning: 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
TypeError: Cannot read properties of null (reading 'useRef')

解决方案

1. 完善库的Peer依赖声明

库中使用了next/link,必须将next也加入peerDependencies,避免库自行安装Next.js导致嵌套的React实例。修改库的package.json:

"peerDependencies": {
  "@chakra-ui/react": "*",
  "react": "*",
  "next": "*"
},
"devDependencies": {
  "@chakra-ui/react": "*",
  "@types/node": "^16.11.12",
  "@types/react": "^18.0.14",
  "@types/react-dom": "^18.0.5",
  "typescript": "^4.4.4"
}

2. 强制统一依赖版本

在宿主项目的package.json中添加resolutions字段,锁定React、ReactDOM、Next.js的版本,确保整个依赖树只有一个版本:

"resolutions": {
  "react": "^18.2.0",
  "react-dom": "^18.2.0",
  "next": "12.2.5"
}

执行yarn install重新安装依赖。

3. 替换本地库安装方式

放弃file://安装,改用Yarn链接避免依赖重复:

  • 在库目录执行:yarn link
  • 在宿主项目目录执行:yarn link my-lib

4. 检查并清理重复依赖

执行yarn list react查看依赖树,确认是否存在多个React版本。若有,找到重复来源(比如某个依赖包自行安装了React),通过resolutions强制统一或调整库的依赖配置。

5. 编译配置优化

确保库的tsconfig.json中skipLibCheck为true,避免类型检查时引入额外依赖;同时编译时不要将React、Next.js等peer依赖打包进产物(tsc默认不会打包peer依赖,无需额外配置)。

内容的提问来源于stack exchange,提问作者Arthur

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.17 17:35:25