reactjs 如何使用图标的snake_case名称(React,material-ui)动态加载图标

lnxxn5zx  于 2023-06-29  发布在  React
关注(0)|答案(5)|浏览(123)

通常我会通过直接导入per the material-ui instructions来使用material-ui图标。
但是我有一个文本标记,它是实际的图标名称(如calendar_view_day),需要动态地从中获取和呈现图标组件。
我怎么能这样的东西:

render() {
  return (
    <Icon name="calendar_view_day" color="primary" />
  )
}

而不是:

render() {
  return (
    <CalendarViewDay color="primary" />  // Imported by name
  )
}
ymzxtsji

ymzxtsji1#

编辑:下面是MUI v4和MUI v5的答案(但未经我测试)
好吧,我想太多了。

正确答案

原来material-ui包含一个图标组件,允许您这样做...它自己转换名字,所以接受snake,pascal和其他变体。* 请注意,这将大大增加捆绑包的大小,正如评论中所指出的那样。如果你的捆绑包大小有限,你将不得不采取不同的方法从某处提供图标SVG!*

import Icon from '@material-ui/core/Icon'

...

render() {
  return (
    <Icon>{props.iconName}</Icon>
  )
}

上一个答案(工作正常,但大规模过度)

创建函数以:

  • 将snake_case转换为PascalCase
  • 处理material-ui图标文档中报告的特殊情况

然后使用material-ui Icon组件。
代码如下:

import Icon from '@material-ui/core/Icon'

function upperFirst(string) {
  return string.slice(0, 1).toUpperCase() + string.slice(1, string.length)
}

function fixIconNames(string) {
  const name = string.split('_').map(upperFirst).join('')
  if (name === '3dRotation') {
    return 'ThreeDRotation'
  } else if (name === '4k') {
    return 'FourK'
  } else if (name === '360') {
    return 'ThreeSixty'
  }
  return name
}

...

render() {
  const iconName = fixIconNames(props.iconName)
  return (
    <Icon>{props.iconName}</Icon>
  )
}
avwztpqn

avwztpqn2#

MUI 5开发人员在这里。

import * as Muicon from "@mui/icons-material";
const Icon = Muicon['SatelliteAlt']
<Icon fontSize="large" sx={{ px: 1 }}/>

typescript

import * as MUIcon from "@mui/icons-material";
interface IconProps {
   icon?: keyof typeof MUIcon;
}
const IconComp: React.FC<IconProps> = ({
  icon,
}) => {
    const Icon = icon && MUIcon[icon];
    return ({Icon && <Icon />})
}
lf5gs5x2

lf5gs5x23#

import React, {createElement } from "react";
import * as MuiIcons from "@material-ui/icons";

export default ({ iconName }) => (
  <div>
      {createElement(MuiIcons[iconName])}
  </div>
);

<ComponentWithIcon iconName="Add" />
<ComponentWithIcon iconName="Anchor" />
vsdwdz23

vsdwdz234#

正如上面对 thclark 的回答的注解所述,使用Icon组件的解决方案将使用Material-UI图标的字体版本,它不包含完整的图标集。
通过使用相同的方法来获取正确的图标名称(借用自 thclark),并结合React.createElement来实现。我在TypeScript中做到了这一点,在vanilla JavaScript中甚至更容易。下面是TS版本的组件(dynamicIcon.tsx),也可以在this repo中使用:

import * as React from 'react';
import * as Icons from '@material-ui/icons/';

type IconType = typeof import('@material-ui/icons/index');

interface DynamicIconProps {
    iconName: string,
    className: string
}

function upperFirst(string:string) {
    return string.slice(0, 1).toUpperCase() + string.slice(1, string.length)
}
  
function fixIconNames(string:string) {
    const name = string.split('-').map(upperFirst).join('')
    if (name === '3dRotation') {
        return 'ThreeDRotation'
    } else if (name === '4k') {
        return 'FourK'
    } else if (name === '360') {
        return 'ThreeSixty'
    }
    return name;
}

export default class DynamicIcon extends React.Component<DynamicIconProps> {
    constructor(props: DynamicIconProps) {
        super(props);
    }

    render() {
        return React.createElement(Icons[fixIconNames(this.props.iconName)! as keyof IconType], {className: this.props.className});
    }
}
yduiuuwa

yduiuuwa5#

从@mui导入整个图标增加了我的构建大小4 MB,这是我不想看到的,所以最后我像这样解决了它

import Icon from "@material-ui/core/Icon";
import * as React from "react";


import { Props } from "./types";

function cameltoSnakeCase(text = "") {
    return text
        .replace(/([A-Z])/g, "_$1")
        .toLowerCase()
        .slice(1);
}

const IconPresentation = ({ glyph, sx }: Props) => {
    if (!glyph) return null;
    return <Icon style={{ ...sx }}>{cameltoSnakeCase(glyph?.toString())}</Icon>;
};

export default IconPresentation;

相关问题