React, динамический выбор компонента для рендеринга
Сегодня хочу разобрать небольшую задачку, которая может кого-то заставить подумать, у меня была необходимость отобразить список элементов в меню, каждый со своей собственной иконкой.
Изначально я сделала глупые элементы для компонента меню, которые выглядели примерно так:
const menu = [
{
title: 'Home',
icon: <HomeIcon className="mr-3 ml-1 h-5 w-5" />
},
{
title: 'Notifications',
icon: <BellIcon className="mr-3 ml-1 h-5 w-5" />
},
{
title: 'Profile',
icon: <UserIcon className="mr-3 ml-1 h-5 w-5" />
},
](планировала использовать очень классный пакет @heroicons/react)
В JSX я просто прошлась по элементам массива меню, чтобы отрисовать {item.icon}.
Но затем мне пришлось изменить классы Tailwind, которые я использовала, в зависимости от состояния приложения.
Поэтому я решила передавать строку вместо самой иконки:
const menu = [
{ title: 'Home', icon: 'HomeIcon' },
{ title: 'Notifications', icon: 'BellIcon' },
{ title: 'Profile', icon: 'UserIcon' },
]
const Icon = (props) => {
const { name } = props
let icon = null
if (name === 'HomeIcon') icon = HomeIcon
if (name === 'BellIcon') icon = BellIcon
if (name === 'UserIcon') icon = UserIcon
return React.createElement(icon, { ...props })
}
...
<Icon name={item.icon} />Другим возможным решением было бы использовать объект для поиска компонентов вместо набора проверок:
const icons = {
HomeIcon,
BellIcon,
UserIcon,
}
const Icon = (props) => {
const { name } = props
const TheIcon = icons[name]
return <TheIcon {...props} />
}
<Icon name={item.icon} />Мне пришлось использовать TheIcon, поскольку компоненты React по соглашению начинаются с заглавной буквы.
Это было достаточно хорошо, но затем я поняла, что могу сделать это гораздо более простым способом, используя фактический компонент вместо строки в массиве меню:
const menu = [
{ title: 'Home', icon: HomeIcon },
{ title: 'Notifications', icon: BellIcon },
{ title: 'Profile', icon: UserIcon },
]
const Icon = (props) => {
const { icon } = props
const TheIcon = icon
return <TheIcon {...props} />
}
...
<Icon icon={item.icon} />Так, казалось бы, изначально достаточно тривиальная задача, но имеет несколько решений. Думаю, если вы только начинаете разбираться с React, то вам это будет полезно!
Не забывайте подписываться на телеграм канал, чтобы не пропустить новые статьи!