自定义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
相关产品推荐
相关产品推荐

