Storybook7 React TS组件库Select组件跨项目导入报错求助
问题排查:React组件库Select组件导入目标项目报错
我基于Storybook 7和TypeScript开发React组件库,通过私有NPM包导入到另一个项目时,Select组件抛出错误:Uncaught Error: Element type is invalid: expected a string (for built-in components) or a class/function ... Check the render method of Select.
组件库内导入逻辑无异常,Storybook中运行正常,其他组件导入也没问题。怀疑和Select内部导入有关,但检查后没发现问题。以下是相关代码:
Select.tsx代码
import React, { Fragment, useState } from 'react'; import { Listbox, Transition } from '@headlessui/react'; import { CheckIcon, SelectorIcon } from '@heroicons/react/solid'; function classNames(...classes: string[]) { return classes.filter(Boolean).join(' '); } type Option = {id?: string | any; text?: string | any; default?: boolean;} // interface to declare all the prop types export interface Props { options?: Option[]; title?: string | any; length?: 'sm' | 'md' | 'lg' | 'fit'; placeholder?: string; className?: string; onChange: (value: string) => void; } // Select component export const Select: React.FC<Props> = ({ options, title, className, length, placeholder, onChange }) => { const selects = options; let selectedOption; let i = 0; selects?.map(select => { if (select.default) { selectedOption = selects[i]; return true; } i++; return false; }); const [selected, setSelected] = useState<any>(selectedOption); return ( <div className={className}> <Listbox value={selected} onChange={(value) => { setSelected(value); onChange(value); }}> {({ open }) => ( <> <Listbox.Label className="block text-sm font-medium text-gray"> {title} </Listbox.Label> <div className={`relative`}> <Listbox.Button className={`cursor-pointer bg-white relative border border-gray-300 rounded-md shadow-sm pl-3 pr-10 py-2 text-left focus:outline-none focus:ring-1 focus:ring-primary/60 focus:border-primary sm:text-sm ${ !length || length === 'md' ? 'w-72' : length === 'fit' ? 'w-full' : length === 'lg' ? 'w-96' : length === 'sm' && 'w-44' }`} > <span className={`block truncate ${ placeholder && !selected ? 'text-gray-400' : 'text-gray' }`} > {selected ? selected.text : placeholder} </span> <span className="absolute inset-y-0 right-0 flex items-center pr-2 pointer-events-none"> <SelectorIcon className="h-5 w-5 text-primary" aria-hidden="true" /> </span> </Listbox.Button> <Transition show={open} as={Fragment} leave="transition ease-in duration-150" leaveFrom="opacity-100" leaveTo="opacity-0" > <Listbox.Options className={`absolute z-10 mt-1 bg-white shadow-lg max-h-60 rounded-md py-1 text-base ring-1 ring-black ring-opacity-5 overflow-auto focus:outline-none sm:text-sm ${ !length || length === 'md' ? 'w-72' : length === 'fit' ? 'w-full' : length === 'lg' ? 'w-96' : length === 'sm' && 'w-44' }`} > {selects?.map(select => ( <Listbox.Option key={select.id} id={select.id} className={({ active }) => classNames( active ? 'text-white bg-primary' : 'text-gray-900', 'cursor-pointer select-none relative py-2 pl-3 pr-9' ) } value={select} > {({ selected, active }) => ( <> <span className={classNames( selected ? 'font-semibold' : 'font-normal', 'block truncate' )} > {select.text} </span> {selected ? ( <span className={classNames( active ? 'text-white' : 'text-primary', 'absolute inset-y-0 right-0 flex items-center pr-4' )} > <CheckIcon className="h-5 w-5" aria-hidden="true" /> </span> ) : null} </> )} </Listbox.Option> ))} </Listbox.Options> </Transition> </div> </> )} </Listbox> </div> ); };
组件库index.ts导出方式
import { Select } from './components/Select'; // ... export { ..., Select };
目标项目导入方式
import { ..., SearchResultHeader, Select } from "@naos/ioe-ui-library";
排查方向
- 依赖打包问题:检查组件库的打包配置(比如rollup、tsconfig),确认
@headlessui/react和@heroicons/react是否被正确处理。如果是外部依赖,需确保目标项目已安装这些包;如果是打包进组件库,要确认打包时没有丢失这些组件的导出。 - 导出/导入冲突:检查组件库中是否有其他同名的
Select导出,或者目标项目中是否有同名变量覆盖了导入的Select。 - TypeScript编译问题:确认组件库的tsconfig是否设置了正确的
module、target,编译后的代码是否正确导出了Select组件。可以直接查看组件库npm包中的编译产物(比如dist目录下的Select.js),确认导出是否正确。 - Fragment使用问题:代码中用了
<>代替Fragment,虽然React支持,但某些打包场景下可能有兼容性问题,尝试替换成<Fragment>测试。 - onChange参数类型不匹配:Select组件的onChange定义为接收
value: string,但实际传递的是整个select对象,这可能导致运行时类型错误,间接引发组件渲染异常。修正类型:将onChange: (value: string) => void改为onChange: (value: Option) => void,并确保调用时传递正确的值。
内容的提问来源于stack exchange,提问作者phil-not-funny
相关产品推荐
相关产品推荐

