Reputation: 165
So i am trying to create a dynamic navigation sidebar for my react app with Material UI. I saved all of the information i should need in a DB. Then i pull the information out of the db and use .map to create the sidebar links. I have everything working besides the " part, those dont render. When mapping the data to my 'template' the following error pops up:
Warning: Failed prop type: Invalid prop `children` of type `string` supplied to `ForwardRef(ListItemIcon)`, expected a single ReactElement.
my source array:
[{…}, {…}, {…}, {…}]
0:{id: 0, label: "Dashboard", link: "/dashboard", icon: "<HomeIcon />"}
1: {id: 1, label: "test", link: "/test", icon: "<SettingsInputComponentIcon />"}
2: {id: 2, label: "test1", link: "/test1", icon: "<TypographyIcon />"}
3: {id: 3, label: "test2", link: "/test2", icon: "<NotificationsIcon />"}
I believe the issue is because the "icon" field is in double quotes. I have been trying to get the icon value out of quotes with with following methods:
for (var i = 0; i < data.length; i++) {
data[i] = data[i].icon.replace(/"/g, "");
or just placing it when i'm mapping like this:
<ListItemIcon>
{icon.replace(/"/g, "")}
</ListItemIcon>
Is there an approved way to do this?
EDIT:
I wanted to point out that i am importing the icons from material UI as follows:
import {
Home as HomeIcon,
NotificationsNone as NotificationsIcon,
FormatSize as TypographyIcon,
ArrowBack as ArrowBackIcon,
SettingsInputComponent as SettingsInputComponentIcon
} from "@material-ui/icons";
Upvotes: 1
Views: 1875
Reputation: 17429
JSX is a superset of JavaScript, meaning that you can't treat JSX as a string like you can with raw HTML.
If you're using Material UI's icons, you need to import their implementation like any other component and use the reference in your data.
import { AccessAlarm, ThreeDRotation } from '@material-ui/icons';
export const nav = [
{ id: 1, label: "test", link: "/test", NavIcon: AccessAlarm },
{ id: 2, label: "test2", link: "/test2", NavIcon: ThreeDRotation }
];
ListItemIcon
children
prop expects a single React Element.
The content of the component, normally
Icon
,SvgIcon
, or a@material-ui/icons
SVG icon element.
An element is a rendered React component, e.g. the plain object returned by React.createElement
. Though we don't want to pre-render these outside the render cycle of React since it'll mess up the lifecycle of the Icon components.
To achieve this, only keep the component type instead of a string.
{
id: 3,
label: "test2",
link: "/test2",
NavIcon: NotificationsIcon // also notice the capital "NavIcon" key.
}
Then, render the icon using its type. Something like the following
{data.map(({ id, label, link, NavIcon }) => (
<ListItem key={id}>
<ListItemText primary={label} />
<ListItemIcon>
<NavIcon />
</ListItemIcon>
</ListItem>
))}
If you really can't change the database format, it's possible to sanitize the icon names and load them by name later using the Material UI Icon
component.
children: The name of the icon font ligature.
If you can keep only the "NotificationsIcon"
part of the string and remove any tag like special characters from the icons value in the database, then it would be only:
import Icon from '@material-ui/core/Icon';
// somewhere
const data = [
{ id: 1, label: "test", link: "/test", icon: "AccessAlarm" },
{ id: 2, label: "test2", link: "/test2", icon: "ThreeDRotation" }
]
// then when rendering
{data.map(({ id, label, link, icon }) => (
<ListItem key={id}>
<ListItemText primary={label} />
<ListItemIcon>
<Icon>{icon}</Icon>
</ListItemIcon>
</ListItem>
))}
But you most keep the name that Material UI is using to identify its icons.
Note that a string doesn't contain its own quotes as part of its value, so .replace(/"/g, "")
does nothing at all and will return the same string, unless it had other "
inside, like quoted string props.
console.log("<SettingsInputComponentIcon />".replace(/"/g, ""))
// => "<SettingsInputComponentIcon />"
Upvotes: 2