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

开发React组件npm包时useRef钩子运行时报错求助

问题描述

开发计划发布为npm包的React组件,打包、发布流程均正常,但导入宿主应用后触发运行时错误:

Warning: Invalid hook call. Hooks can only be called inside of the body of a function component.
Uncaught TypeError: Cannot read properties of null (reading 'useRef')

直接在宿主应用中编写组件代码可正常运行,但作为npm包导入时,useRef的current始终无法被赋值。

已尝试的临时方案

  • 将函数组件转为Class PureComponent,用callback ref实现相同逻辑,组件可正常工作,排除React版本不匹配问题。
  • 改用自定义Hook管理callback ref,发布后宿主应用可正常使用,但本地开发仍报错;需通过yarn link/npm link同时链接包及其peer依赖(如react)才能正常运行。
问题根源

核心原因是npm包和宿主应用加载了两份独立的React实例。React的Hook机制依赖单一全局React实例维护组件状态,多实例存在时,Hook校验逻辑失效,导致useRef等钩子无法正常工作,触发"Invalid hook call"错误。

完整解决方案

1. 打包配置(以Rollup为例)

确保打包时不将React、ReactDOM等依赖打包进组件包,由宿主应用提供这些依赖:

  • 在rollup.config.js中配置external字段排除peer依赖:
export default {
  // ...其他配置
  external: ['react', 'react-dom'],
  output: {
    // ...其他配置
    globals: {
      react: 'React',
      'react-dom': 'ReactDOM'
    }
  }
}
  • 在package.json中声明peerDependencies,明确要求宿主应用提供兼容版本的React:
{
  "peerDependencies": {
    "react": "^16.8.0 || ^17.0.0 || ^18.0.0",
    "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0"
  },
  "devDependencies": {
    "react": "^18.0.0",
    "react-dom": "^18.0.0"
    // ...其他开发依赖
  }
}

2. 本地开发环境处理

避免npm link/yarn link导致的重复React实例:

  • 在组件包根目录执行命令,将本地React依赖指向宿主应用的React:
# npm
npm link ../宿主应用路径/node_modules/react
npm link ../宿主应用路径/node_modules/react-dom

# yarn
yarn link ../宿主应用路径/node_modules/react
yarn link ../宿主应用路径/node_modules/react-dom
  • 或在宿主应用中强制统一React版本:
// yarn的package.json
"resolutions": {
  "react": "^18.0.0",
  "react-dom": "^18.0.0"
}

// npm的package.json
"overrides": {
  "react": "^18.0.0",
  "react-dom": "^18.0.0"
}

3. 代码层面规范

  • 严格遵循React Hook规则:仅在函数组件或自定义Hook的顶层调用Hook,禁止在条件判断、循环、嵌套函数中调用。
  • 自定义Hook管理ref的示例:
import { useRef, useEffect } from 'react';

export function useMyRef() {
  const ref = useRef(null);

  useEffect(() => {
    console.log('ref current:', ref.current);
  }, []);

  return ref;
}

// 组件中使用示例
function MyComponent() {
  const myRef = useMyRef();
  return <div ref={myRef}>测试组件</div>;
}

内容的提问来源于stack exchange,提问作者Matteo Pietro Peru

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 10:20:11