React
June 4, 2022

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, то вам это будет полезно!

Не забывайте подписываться на телеграм канал, чтобы не пропустить новые статьи!