Следуя документу Гэтсби по Создание динамической навигации в Gatsby, я создал простое меню и хотел посмотреть, смогу ли я добавить в него компоненты Реагировать на иконки:
gatsby-config.js (урезанный)
menuLinks:[
{
name:'home',
icon: 'AiFillHome',
link:'/'
},
{
name:'contact',
icon: 'AiFillHome',
link:'/contact'
}
]
после того, как узнал, что Gatsby выдает ошибку, когда я пытался создать внешний файл menuLinks в качестве модуля, например:
неудачный подход как модуль:
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, поэтому рендеринг его как компонента React в <Icon /> должен помочь, потому что это сам элемент.
Есть еще один обходной путь, который кажется немного излишним, но будет работать: вы всегда можете использовать 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 и использовать name напрямую в AssetComponentsTuple, в этом случае: AssetComponentsTuple.get(name)
Хм... как насчет {React.createElement(Icon)}
тот же результат рендеринга <aifillhome></aifillhome>
Я добавил обходной путь Map, пока пытаюсь найти другой способ:/
Map не работал у меня, но дал мне идею создать компонентное решение, так что +1 и, как всегда, приятно поговорить с вами о Гэтсби.
Реализация:
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 или любой компонент в этом отношении и возвращать его на основе строки значка.
Это ближе, но все еще не отображается в браузере. Сделал правку с деталями.