【问题标题】:React and Typescript dynamic component from config来自配置的 React 和 Typescript 动态组件
【发布时间】:2021-08-13 05:28:50
【问题描述】:

我正在尝试使用 reactjs 和 typescript 构建一个动态菜单系统。

我的配置如下所示:

import {TableIcon} from "@heroicons/react/solid";

type route = {
    icon: React.ReactNode,
    path: string,
    title: string,
}

export const navRoutes = () : route[] => {
    return [
        {
            icon: TableIcon,
            path: '/',
            title: 'Home'
        },
    ]
}

在我的导航组件中,我正在做

{navRoutes().map((item) => (
    <a key={item.title} href={item.path}>
        <item.icon /> // also tried {item.icon}
        {item.title}
    </a>
))}

我收到一个错误,TS2604: JSX element type 'item.icon' does not have any construct or call signatures.

我在没有按预期工作的打字稿的情况下做了类似的事情——谁能告诉我我做错了什么?

【问题讨论】:

    标签: javascript reactjs typescript tailwind-css


    【解决方案1】:

    React.ReactNode 是渲染组件的类型。例如:

    const a: React.ReactNode = <TableIcon />
    

    如果你想传入一些渲染 JSX,那就是你会使用的。但听起来你想传入一个没有道具的功能组件,然后然后被渲染。

    React.FC 是没有道具的反应功能组件的通用类型。这可能就是你想要的类型。

    type route = {
        icon: React.FC,
        path: string,
        title: string,
    }
    

    Playground

    【讨论】:

      【解决方案2】:

      React.ReactNode 类型描述了调用 JSX 组件而不是组件本身的返回值。您可以:

      1 ) 传递一个React.ReactNode:

      type route = {
        icon: React.ReactNode;
        path: string;
        title: string;
      };
      
      export const navRoutes = (): route[] => {
        return [
          {
            icon: <TableIcon />,
            path: "/",
            title: "Home"
          }
        ];
      };
      
      {navRoutes().map((item) => (
        <a key={item.title} href={item.path}>
          {item.icon}
          {item.title}
        </a>
      ))}
      
      1. 传递一个可调用的组件。通常你需要给它一个大写的名字以便通过 JSX 调用它。不知何故&lt;item.icon/&gt; 似乎有效?但为了安全起见,我会大写。
      type route = {
        icon: React.ComponentType;
        path: string;
        title: string;
      };
      
      export const navRoutes = (): route[] => {
        return [
          {
            icon: TableIcon,
            path: "/",
            title: "Home"
          }
        ];
      };
      
      {navRoutes().map(({title, path, icon: Icon}) => (
        <a key={title} href={path}>
          <Icon/>
          {title}
        </a>
      ))}
      

      默认情况下React.ComponentType 不使用任何道具(除了孩子)。如果您想在调用&lt;Icon/&gt; 时传递道具,可以使用React.ComponentType&lt;SomePropsType&gt;

      【讨论】:

        【解决方案3】:

        您可以使用 JSX.Element 代替 React.ReactNode,这就是 @heroicons/react 作为类型返回的内容

        页脚社交图标(为本示例添加了 TableIcon)

        import { FC } from 'react';
        import cn from 'classnames';
        import {
            Facebook,
            Instagram,
            SquareLogo
        } from '@/components/UI/Icons';
        import { TableIcon } from '@heroicons/react/solid';
        import css from './footer.module.css';
        
        export interface FooterSocialProps {
            href: string;
            label: string;
            id: number;
            icon: JSX.Element;
        }
        
        export const footerSocial: FooterSocialProps[] = [
            {
                href: 'https://www.facebook.com/thefaderoominc/?ref=py_c',
                label: 'Facebook',
                id: 0,
                icon: <Facebook />
            },
            {
                href: 'https://www.instagram.com/thefaderoomhighlandpark/',
                label: 'Instagram',
                id: 1,
                icon: <Instagram />
            },
            {
                href: 'https://squareup.com/gift/MLHZCDVC0MKB1/order',
                label: 'Square Giftcards',
                id: 2,
                icon: <SquareLogo />
            },
            {
                href: 'https://stackoverflow.com/example',
                label: 'Table Icon',
                id: 3,
                icon: <TableIcon />
            }
        ];
        
        export interface FooterSocialPropsFC {
            className?: string;
        }
        
        const FooterSocial: FC<FooterSocialPropsFC> = ({
            className
        }) => {
            return (
                <div className={cn(css.socialRoot, className)}>
                    {footerSocial.map((social, i) => (
                        <div key={++i}>
                            <a
                                title={social.label}
                                target='__blank'
                                href={social.href}
                                className={cn(
                                    css.socialLink,
                                    'text-olive-300 hover:text-olive-400  text-opacity-80'
                                )}
                            >
                                <span className='sr-only'>
                                    {`External Link to The Fade Room's ${social.label} page`}
                                </span>
                                {social.icon}
                            </a>
                        </div>
                    ))}
                </div>
            );
        };
        
        export default FooterSocial;
        
        

        【讨论】:

          猜你喜欢
          • 2022-07-04
          • 1970-01-01
          • 1970-01-01
          • 2020-07-02
          • 2022-01-26
          • 2020-05-16
          • 2020-08-12
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多