我有一個需求,需要在菜單中渲染一個項目列表,每個項目都有自己的圖標。

起初,我在菜單中硬編碼組件,像這樣:

const menu = [
  {
    title: '首頁',
    icon: <HomeIcon className="mr-3 ml-1 h-5 w-5" />
  },
  {
    title: '通知',
    icon: <BellIcon className="mr-3 ml-1 h-5 w-5" />
  },
  {
    title: '個人資料',
    icon: <UserIcon className="mr-3 ml-1 h-5 w-5" />
  },
];

使用非常酷的 @heroicons/react 套件。

在 JSX 中,我使用 menu 陣列迭代顯示 {item.icon}

但是,後來我需要根據應用程序的響應狀態更改使用的 Tailwind 類,所以我使用了 react-responsive 套件。

我決定改為傳遞一個字符串,因此首先做了這個更改:

const menu = [
  { title: '首頁', icon: 'HomeIcon' },
  { title: '通知', icon: 'BellIcon' },
  { title: '個人資料', 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} />

另一種解決方案是使用物件查找組件,而不是一堆 if 檢查:

const icons = {
  HomeIcon,
  BellIcon,
  UserIcon,
};

const Icon = (props) => {
  const { name } = props;

  const TheIcon = icons[name];
  return <TheIcon {...props} />;
};

<Icon name={item.icon} />

我必須使用 TheIcon,因為 React 組件以大寫字母開頭是一種約定。

這樣已經足夠好了,但後來我意識到可以以更簡潔的方式來實現,直接在 menu 陣列中使用實際的組件而不是字符串:

const menu = [
  { title: '首頁', icon: HomeIcon },
  { title: '通知', icon: BellIcon },
  { title: '個人資料', icon: UserIcon },
];

const Icon = (props) => {
  const { icon } = props;
  const TheIcon = icon;
  return <TheIcon {...props} />;
};

...

<Icon icon={item.icon} />