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

自定义npm包中使用lottie-react触发React组件类型错误

问题:React组件提取到私有包后Lottie渲染崩溃

问题描述

我开发了一个TextDisplay React TypeScript组件,支持文本省略、悬停显示复制按钮,点击复制后通过lottie-react展示2.5秒动画。组件在本地Next.js TypeScript项目中运行正常,但提取到GitHub私有包后,组件能正常渲染,但点击复制按钮(渲染Lottie动画时)应用崩溃,报错React.createElement类型无效。

错误信息

Warning: React.createElement: type is invalid -- expected a string (for built-in components) or a class/function (for composite components) but got: object.

Check the render method of `ClipboardCopy`.
    at ClipboardCopy (webpack-internal:///../internal-packages/components/core/dist/esm/TextDisplay/TextDisplay.js:285:19)
    ...(省略后续调用栈)

组件代码

import React from 'react';
import { Text, TextProps, Stack, CSSObject } from '@mantine/core';
import { CSSProperties, useState } from 'react';
import Lottie from 'lottie-react';
import GreenCheckAnimation from '../resources/lottie/greenCheck.json';

interface TextDisplayProps extends TextProps {
  children: string | undefined;
  maxWidth?: string | number;
}

const defaultStyle = {
  position: 'relative',
  paddingInlineEnd: '40px',
  marginInlineEnd: '-40px',
  overflow: 'hidden',
  textOverflow: 'ellipsis',
} as CSSProperties;

const textContainerStyle = {
  width: '100%',
  height: 'calc(100% + 10px) !important',
  position: 'absolute',
  top: -5,
  left: -5,
  '&:hover > div > div': {
    opacity: 1,
  },
} as CSSObject;

const clipboardContainerStyle = {
  width: '24px',
  height: '24px',
  cursor: 'pointer',
  position: 'absolute',
  display: 'flex',
  justifyContent: 'center',
  alignItems: 'center',
  right: 5,
  top: 0,
  bottom: 0,
  marginTop: 'auto',
  marginBottom: 'auto',
} as CSSObject;

const iconContainer = {
  opacity: 0,
  transition: 'opacity 0.1s ease',
} as CSSObject;

const TextDisplay = ({ maxWidth, ...props }: TextDisplayProps) => {
  const [copied, setCopied] = useState(false);

  const copyToClipboard = async (e: any) => {
    e.stopPropagation();
    setCopied(true);

    if (props?.children) {
      await navigator.clipboard.writeText(props?.children);
    }

    setTimeout(() => setCopied(false), 2500);
  };

  return (
    <Text {...props} style={{ ...defaultStyle, ...props.style, maxWidth }}>
      {props.children}
      <ClipboardCopy copied={copied} copyToClipboard={copyToClipboard} />
    </Text>
  );
};

const ClipboardCopy = ({
  copied,
  copyToClipboard,
}: {
  copied: boolean;
  copyToClipboard: (e: any) => Promise<void>;
}) => {
  return (
    <Stack sx={textContainerStyle}>
      <Stack sx={clipboardContainerStyle}>
        {!copied && (
          <Stack sx={iconContainer} onClick={copyToClipboard}>
            <div>copy</div>
          </Stack>
        )}
        {copied && (
          <Lottie
            animationData={GreenCheckAnimation}
            loop={false}
            style={{ width: '30px', height: '30px' }}
          />
        )}
      </Stack>
    </Stack>
  );
};

export default TextDisplay;

私有包package.json

{
  "name": "@mypackage/core",
  "version": "0.0.1",
  "description": "Generic core UI components to be used across my sites",
  "main": "dist/esm/index.js",
  "types": "dist/types/index.d.ts",
  "type": "module",
  "files": [
    "dist"
  ],
  "exports": {
    ".": {
      "import": "./dist/esm/index.js",
      "types": "./dist/types/index.d.ts",
      "default": "./dist/esm/index.js"
    }
  },
  "scripts": {
    "test": "exit 0",
    "build": "tsc"
  },
  "author": "Me",
  "license": "ISC",
  "dependencies": {
    "lottie-react": "^2.4.0",
    "react": "^18.2.0",
    "react-dom": "^18.2.0"
  },
  "peerDependencies": {
    "@mantine/core": "6.0.21",
    "@mantine/hooks": "6.0.21",
    "react": "18.2.0",
    "react-dom": "18.2.0"
  },
  "devDependencies": {
    "@types/node": "18.15.3",
    "@types/react": "18.0.28",
    "typescript": "^5.3.3"
  }
}

已尝试操作

  • 打印Lottie组件和JSON文件,确认JSON导入正常,Lottie看起来是有效JSX对象
  • 对比Mantine Text组件结构,与Lottie一致
  • 直接使用包中的TS代码,问题依旧

解决方案

1. 让Next.js编译私有包

Next.js默认不会编译node_modules中的包,私有包的ES模块语法或依赖可能无法被正确解析。在Next.js项目的next.config.js中添加transpilePackages配置:

/** @type {import('next').NextConfig} */
const nextConfig = {
  transpilePackages: ['@mypackage/core'],
}

module.exports = nextConfig;

2. 完善私有包的TypeScript配置

创建或修改私有包根目录的tsconfig.json,确保正确输出ES模块并处理JSON文件:

{
  "compilerOptions": {
    "target": "ES2020",
    "module": "ESNext",
    "moduleResolution": "node",
    "lib": ["ES2020", "DOM", "DOM.Iterable"],
    "jsx": "react-jsx",
    "declaration": true,
    "declarationDir": "./dist/types",
    "outDir": "./dist/esm",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "resolveJsonModule": true,
    "isolatedModules": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "dist"]
}

3. 复制静态资源到构建目录

TypeScript编译器不会自动复制JSON等非代码文件,需要修改私有包的package.json脚本,添加复制静态资源的命令:

"scripts": {
  "test": "exit 0",
  "build": "tsc && cp -r src/resources dist/esm/resources"
}

4. 统一依赖版本

检查Next.js项目和私有包中lottie-react的版本是否一致,避免版本冲突。在Next.js项目中执行:

npm ls lottie-react

如果存在多个版本,统一升级或降级到相同版本。

5. 验证Lottie导入方式

尝试将Lottie的默认导入改为命名导入(部分场景下包的导出可能存在兼容问题):

// 原导入
import Lottie from 'lottie-react';
// 修改为
import { Lottie } from 'lottie-react';

完成以上步骤后,重新构建私有包并更新Next.js项目中的依赖,问题应该可以解决。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 20:25:53