'I am trying to dynamically create a Mui icon using React.createElement. However, React lowercases the element. Is there a way to keep case?

I am getting service.icon from JSON, so it looks like so

[
  {
    "icon": "AdminPanelSettingsIcon",
  }
]

I am using React.createElement() like so,

data.map((service) => {
  let icon = React.createElement(
    service.icon, 
    { key: service.icon }, 
    null);
});

my output is <adminpanelsettingsicon></adminpanelsettingsicon>

is there anyway to keep case, or convert to PascalCase so it renders like so <AdminPanelSettingsIcon></AdminPanelSettingsIcon>

Note: Is there anyway to dynamically display mui icons based on specified JSON aka api call.



Solution 1:[1]

The docs specify:

React.createElement(
  type,
  [props],
  [...children]
)

The type argument can be either a tag name string (such as 'div' or 'span'), a React component type (a class or a function), or a React fragment type.

You're attempting to create an element using the string "AdminPanelSettingsIcon". Instead you should be attempting this by passing in the class:

React.createElement(AdminPanelSettingsIcon)

You can do this by converting your list to use classes as the value:

[
  {
    // The value is a class now, not a string
    "icon": AdminPanelSettingsIcon,
  }
]

Solution 2:[2]

Update:

If you are using https://www.npmjs.com/package/@material-ui/icons (or any other library), you can dynamically import all of the components like this:

//Preparing, it should run inside async function
let maps = {}
let listIcon = [];
await import('@material-ui/icons').then(res => {
      listIcon = res
})

for(let icon of listIcon){
    maps[icon.constructor.name.toLowerCase()] = icon;
}

//Render
data.map((service) => {
    const component = maps[service.icon] || DefaultComponent;
    let icon = React.createElement(
        component,
        { key: component },
        null);
});

Original answer:

If the API output is predictable (service.icon is just a component of the components you defined)

You can do this:

//Import...

//Defined maps
const maps = {
    "adminpanelsettingsicon": AdminPanelSettingsIcon,
    "adminpanelsettingsicon2": AdminPanelSettingsIcon2,
    "adminpanelsettingsicon3": AdminPanelSettingsIcon3,
}

//Render
data.map((service) => {
    const component = maps[service.icon] || DefaultComponent;
    let icon = React.createElement(
        component,
        { key: component },
        null);
});

It'll work as you wish

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2