繁体   English   中英

来自配置的 React 和 Typescript 动态组件

[英]React and Typescript dynamic component from config

我正在尝试使用 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.

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

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

const a: React.ReactNode = <TableIcon />

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

React.FC是没有 props 的 react 功能组件的通用类型。 这可能就是你想要的类型。

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

操场

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 调用它。 不知何故<item.icon/>似乎有效? 但为了安全起见,我会使用它。
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不接受任何道具(除了孩子)。 如果你想在调用<Icon/>时传递道具,你可以使用React.ComponentType<SomePropsType>

您可以使用 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;

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM