在 Gatsby 中如何从 gatsby-config 的 menuLinks 动态渲染 React 图标?

In Gatsby how to render React Icons dynamically from gatsby-config's menuLinks?

遵循 Gatsby 的 Creating Dynamic Navigation in Gatsby I created a barebones menu and wanted to see if I can add React Icons' 组件文档:

gatsby-config.js(精简)

menuLinks:[
  {
      name:'home',
      icon: 'AiFillHome',
      link:'/'
  },
  {
      name:'contact',
      icon: 'AiFillHome',
      link:'/contact'
  }
]

在我尝试创建外部 menuLinks 文件作为模块时发现 Gatsby 出错后,示例:

作为模块的方法失败:

import React from 'react'

// React Icons
import { AiFillHome } from 'react-icons/ai'

const Menu = [
  {
      name:'home',
      icon: <AiFillHome />,
      link:'/'
  },
  {
      name:'contact',
      icon: <AiFillHome />,
      link:'/contact'
  }
]

export default Menu

我的查询没有问题:

const data = useStaticQuery(
  graphql`
    query {
      site {
        siteMetadata {
          menuLinks {
            name
            icon
            link
          }
        }
      }
    }
  `,
)

在一个文件中,我已将菜单道具从我的查询传递到然后映射。

(精简文件):

{menu.map((menuItem, key) => {
    return (
      <Link key={key} to={menuItem.link}>
        <div>
          <span className="icon">{`<${menuItem.icon} />`}</span>
          {menuItem.name}
        </div>
      </Link>
    )
})}

我的 icon 不呈现。我也试过:

<span className="icon" component={menuItem.icon} />

不起作用。我也试过:

<span className="icon"><menuItem.icon /></span>

和:

<span className="icon">{React.createElement(menuItem.icon)}</span>

研究:

在 Gatsby 中,如何将图标的组件名称传递给 menuLinks 并稍后呈现它?

编辑

执行后:

{menu.map(({link, name, icon: Icon}) => {
    return (
      <Link key={name} to={link}>
        <div>
          <span className="icon"><Icon /></span>
          {name}
        </div>
      </Link>
    )
})}

浏览器不呈现 React 图标组件,在检查元素面板时我得到:

<span class="icon">
  <aifillhome></aifillhome>
</span>

另请注意,我确实收到以下终端错误:

warning 'AiFillHome' is defined but never used no-unused-vars

这是预料之中的,但这让我想知道我该如何引入:

import { AiFillHome } from 'react-icons/ai'

尝试这样的事情:

{menu.map(({link, name, icon: Icon}) => {
    return (
      <Link key={name} to={link}>
        <div>
          <span className="icon"><Icon /></span>
          {name}
        </div>
      </Link>
    )
})}

由于您正在遍历 menu 元素,因此 icon 由于大写而未被解释为 React 元素。在上面的解构中,您将 icon 解析为 Icon,因此将其呈现为 <Icon /> 中的 React 组件应该可以解决问题,因为它是元素本身。

还有另一种解决方法似乎有点矫枉过正但会起作用:您始终可以使用 Map 来保存资产组件,例如:

const AssetComponentsTuple = new Map([
  [`home`, <AiFillHome />],
  [`otherAsset`, <AiFillHome2 />],
]);

export default AssetComponentsTuple;

您的菜单将变为:

const Menu = [
  {
      name:'home',
      icon: 'home',
      link:'/'
  },
  {
      name:'contact',
      icon: 'otherAsset',
      link:'/contact'
  }
]

然后在循环中:

{menu.map(({link, name, icon}) => {
   let IconComponent = AssetComponentsTuple.get(icon);
    return (
      <Link key={name} to={link}>
        <div>
          <span className="icon"><IconComponent /></span>
          {name}
        </div>
      </Link>
    )
})}

你甚至可以去掉icon属性,直接使用AssetComponentsTuple中的name,在这种情况下:AssetComponentsTuple.get(name)

实施:

const AssetComponentsTuple = new Map([
  [`home`, <AiFillHome />],
  [`otherAsset`, <AiFillHome2 />],
])

export default AssetComponentsTuple

会抛出以下错误:

Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: object.

但该方法让我想到了以下解决方法:

{menu.map(({link, name, icon}) => {
    return (
      <Link key={name} to={link}>
        <div>
          <span className="icon"><IconRetrieve icon={icon} /></span>
          {name}
        </div>
      </Link>
    )
})}

这让我可以为组件传递图标字符串和三元组。

IconRetrieve 分量:

import React from 'react'
import PropTypes from 'prop-types'

// React Icons
import { AiFillHome } from 'react-icons/ai'
import { MdCall } from 'react-icons/md'
import { BiErrorCircle } from 'react-icons/bi'

const IconRetrieve = ({ icon }) => {
  const mapIcons = new Map([
    [`home`, <AiFillHome />],
    [`contact`, <MdCall />],
    [`default`, <BiErrorCircle />],
  ])
  return mapIcons.has(icon) ? mapIcons.get(icon) : mapIcons.get('default')
}

IconRetrieve.propTypes = {
  icon: PropTypes.string.isRequired,
}

export default IconRetrieve

这种方法的优点是我可以创建 SVG 组件或与此相关的任何组件,并且 return 它基于图标字符串。