В Гэтсби, как динамически отображать значки React из меню ссылок gatsby-config?

Следуя документу Гэтсби по Создание динамической навигации в 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'
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Навигация по приложениям React: Исчерпывающее руководство по React Router
Навигация по приложениям React: Исчерпывающее руководство по React Router
React Router стала незаменимой библиотекой для создания одностраничных приложений с навигацией в React. В этой статье блога мы подробно рассмотрим...
Массив зависимостей в React
Массив зависимостей в React
Все о массиве Dependency и его связи с useEffect.
1
0
78
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Попробуйте что-то вроде этого:

{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)

Это ближе, но все еще не отображается в браузере. Сделал правку с деталями.

DᴀʀᴛʜVᴀᴅᴇʀ 01.04.2022 15:08

Хм... как насчет {React.createElement(Icon)}

Ferran Buireu 01.04.2022 16:36

тот же результат рендеринга <aifillhome></aifillhome>

DᴀʀᴛʜVᴀᴅᴇʀ 01.04.2022 16:38

Я добавил обходной путь Map, пока пытаюсь найти другой способ:/

Ferran Buireu 01.04.2022 16:44

Map не работал у меня, но дал мне идею создать компонентное решение, так что +1 и, как всегда, приятно поговорить с вами о Гэтсби.

DᴀʀᴛʜVᴀᴅᴇʀ 01.04.2022 17:39
Ответ принят как подходящий

Реализация:

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 или любой компонент в этом отношении и возвращать его на основе строки значка.

Другие вопросы по теме