Reputation: 10422
I'm trying to build a dynamic menu system with reactjs and typescript.
My config looks like this:
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'
},
]
}
in my nav component, I'm doing
{navRoutes().map((item) => (
<a key={item.title} href={item.path}>
<item.icon /> // also tried {item.icon}
{item.title}
</a>
))}
I'm getting an error that TS2604: JSX element type 'item.icon' does not have any construct or call signatures.
I've done similar things without typescript that worked as expected-- can anyone tell me what I am doing wrong?
Upvotes: 0
Views: 1854
Reputation: 1158
You can use JSX.Element instead of React.ReactNode, that's what @heroicons/react
returns as a type
Footer Social Icons (added the TableIcon for this example)
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;
Upvotes: 1
Reputation: 42288
The type React.ReactNode
describes the returned value from calling a JSX component rather than the component itself. You can either:
1 ) Pass a 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>
))}
<item.icon/>
seems like it works? But to be safe I would capitalize it.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>
))}
By default React.ComponentType
doesn't take any props (other than children). If you want to pass props when you call your <Icon/>
you can use React.ComponentType<SomePropsType>
.
Upvotes: 2
Reputation: 187262
React.ReactNode
is the type of a rendered component. For example:
const a: React.ReactNode = <TableIcon />
If you want to pass in some rendered JSX, that's what you would use. But it sounds like you want to pass in a a functional component with no props, which will then be rendered.
React.FC
is the generic type of a react functional component with no props. That's probably the type you want.
type route = {
icon: React.FC,
path: string,
title: string,
}
Upvotes: 2