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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 15:15:36