Render React Element within Array - arrays

I have an array of items, which are defined by a string and an icon component:
type MyItem = {
Title: string;
Icon: React.ElementType;
};
export const MyItems: MyItem[] = [
{ Title: "Inbox", Icon: InboxIcon },
{ Title: "Users", Icon: UserIcon },
{ Title: "Settings", Icon: SettingsIcon },
];
Now I want to display them within a component:
<>
{MyItems.map((item, index) => (
<h1>item.Title</h1>
// render icon component here: item.Icon
))}
</>
How can I render the icon component (without changing the array structure)?

I'm guessing here that your icons are essentially React Components. If so then you can try out the code below:
<>
{MyItems.map((item, index) => (
<>
<h1>item.Title</h1>
{item.Icon}
</>
))}
</>

Related

Using react-icons that are imported from another file

In a react app (created by vite), I need a list of categories to create a navigation menu. I have created this list in another file with .js extension. In the form of an array of objects:
// constants.js
import { MdHomeIcon, MdCodeIcon } from "react-icons/md";
export const categories = [
{ name: "New", icon: <MdHomeIcon /> },
{ name: "Coding", icon: <MdCodeIcon /> },
{ name: "ReactJS", icon: <MdCodeIcon /> },
];
react-icons package is installed.
My problem is this: before doing anything, react app return a syntax error in console: Uncaught SyntaxError: expected expression, got '<' in constants.js.
after solving the problem: In Sidebar.jsx, I want to map through categories to build a list with names and icons.:
// Sidebar.jsx
import { categories } from "./constants.js";
export const Sidebar = () => (
<div>
{categories.map((category) => (
<div>
<span>{category.icon}</span>
<span>{category.name}</span>
</div>
))}
</div>
);
You can't include JSX inside a js file but your icons are being imported from "react-icons/md" and you use them as JSX elements.
If you want to use these icons inside of constants.js try renaming it to constants.jsx
There is an error on the map loop.
You can't return 2 children in the map loop.
One way to solve it is using React Fragment to wrap up those components.
https://reactjs.org/docs/fragments.html
// Sidebar.jsx
import { categories } from "./constants.js";
export const Sidebar = () => (
<div>
{categories.map((category) => (
<React.Fragment>
<span>{category.icon}</span>
<span>{category.name}</span>
</React.Fragment>
))}
</div>
);
Another thing, you must be alert to the key prop inside lists and loops.
https://reactjs.org/docs/lists-and-keys.html
Third thing. Attach the Icon call as the value of your obj, and then call this as a react component
// Sidebar.jsx
import { categories } from "./constants.js";
export const Sidebar = () => (
<div>
{categories.map((category) => {
const IconComponent = category.icon;
return (
<> // another way to use fragment
<span>
<IconComponent />
</span>
<span>{category.name}</span>
</>
)}
)}
</div>
);
Last but not least, there is a syntax error in your code. There is a } missing in the final line of the loop
//data categories.js
export const categories = [
{ name: "New", icon: "Home" },
{ name: "Coding", icon: "Code" },
{ name: "ReactJS", icon: "Code" },
];
//sidebar.js
import { categories } from "./constants.js";
import { MdHomeIcon, MdCodeIcon } from "react-icons/md";
export const Sidebar = () => (
<div>
{categories && categories.map((category, i) => (
<div key = {i}>
{category.icon === "Code"? <MdCodeIcon/> : <MdHomeIcon/>}
<span>{category.name}</span>
<div/>
))}
</div>
);
you can edit the code as needed

How to use "react-icons" in loop (Map method) in ReactJS 😅

I'm using React-icons in my ReactJS project and I just wanted to loop (by Map method) the specific icons in each JSX field when data is render.
In other word, I want this{`<${e.contact.icons}/>`}in JSX code.
Here is my code section:-
Here is, I import some icons for React icons.
import { FaBeer, Fa500Px, FeAccusoft } from "react-icons/fa";
Here is a data array which I want to render in JSX.
const data = [
{
contact: [
{
title: 'contact',
icons: 'FaBeer',
},
{
title: 'contact',
icons: 'Fa500Px',
},
{
title: 'contact',
icons: 'FaAccusoft',
},
],
},
]
And this is my component in down below. Which I'm using icons. You get little idea what I want to do.
const contact = () => {
return (
<>
{data.map((e, i) => {
return (
<>
<div className="text-area">
<span> {`<${e.contact.icons}/>`} </span>
</div>
</>
);
})}
</>
);
};
export default contact;
I'm trying to use like this{`<${e.contact.icons}/>`}, but is not working. When I see in browser. It's look like this.
<FaBeer/>
<Fa500Px/>
<FaAccusoft/>
It's just return like a text, but I want to get icons.
Any suggestion ?
You cannot use strings to represent React Component Types, instead you can use the imported ComponentType itself.
import { FaBeer, Fa500Px, FaAccusoft } from "react-icons/fa";
// here is data for I want to show
const data = [
{
contact: [
{
title: "contact",
subtitle: "get in touch",
icons: FaBeer,
},
{
title: "contact",
subtitle: "get in touch",
icons: Fa500Px,
},
{
title: "contact",
subtitle: "get in touch",
icons: FaAccusoft,
},
],
},
];
const Contact = () => {
return (
<>
{data.map((e, i) => {
const Icon = e.contact.icons;
return (
<>
<div className="text-area">
<h1 className="title">{e.contact.title}</h1>
<h2 className="subtitle">{e.contact.subtitle}</h2>
<span><Icon /></span>
</div>
</>
);
})}
</>
);
};
export default Contact;
Note how the rendering of the icon changes as well. I have assigned the icon component to a variable Icon instead of calling <e.contact.icons/> directly because React expects components to start with a capital letter.
The Icon variable will be a React component (either a function component or a class component) so you can call that component by using standard JSX <Icon /> syntax. You can also pass any of the react-icons props, for example: <Icon color="#FF0000" size={24}/>.
https://codesandbox.io/s/fervent-goldwasser-y83cn?file=/src/App.js
import { FaBeer, Fa500Px, FaAccusoft } from "react-icons/fa";
// here is data for I want to show
const data = [
{
contact: [
{
title: "contact",
subtitle: "get in touch",
icons: FaBeer
},
{
title: "contact",
subtitle: "get in touch",
icons: Fa500Px
},
{
title: "contact",
subtitle: "get in touch",
icons: FaAccusoft
}
]
}
];
const contact = () => {
return (
<>
{data.map((e, i) => {
return (
<>
{e.contact.map((e, i) => {
return (
<div className="text-area" key={i}>
<h1 className="title">{e.title}</h1>
<h2 className="subtitle">{e.subtitle}</h2>
<span>
<e.icons />
</span>
</div>
);
})}
</>
);
})}
</>
);
};
export default contact;
Well, the option of importing FaIcon-s and putting them into "data" array looks pretty nice:
import { FaBeer, Fa500Px, FaAccusoft } from "react-icons/fa";
const data = [
{
contact: [
{
title: "contact",
subtitle: "get in touch",
icons: FaBeer,
},
...
On the other hand possibility of generating components "dynamically" by their string name could be still implemented.
Firstly, I find usefull following article: React / JSX Dynamic Component Name
Next, I've created a new FaIconDynamic component:
import {
AiFillAccountBook,
AiFillAlert,
AiFillAlipayCircle,
AiFillAndroid,
} from 'react-icons/ai';
export const FaIconDynamic = ({ type }) => {
const FaIcon = components[type];
return <FaIcon></FaIcon>;
};
const components = {
AiFillAccountBook,
AiFillAlert,
AiFillAlipayCircle,
AiFillAndroid,
};
And then that's pretty easy to generate any registered above fa-icon, f.e.:
function App() {
return (
<>
<FaIconDynamic type={'AiFillAccountBook'} />
<FaIconDynamic type={'AiFillAlert'} />
<FaIconDynamic type={'AiFillAlipayCircle'} />
<FaIconDynamic type={'AiFillAndroid'} />
</>
);
}
Of course, both approaches have their pros and cons and could be more benefitial in some situations over each other
I have got the answer. I know the answer is not an ideal one, but it's work for me just now. The problem with the answer is that. We imported all the fonts from react-icons. So, I guess, as we will grow the project larger. It will decrease the performances and the major factor of could be react icons.
And also as Mr.Ali Shefaee describe in the comment section.
import React from "react";
import { render } from "react-dom";
import * as FontAwesome from "react-icons/lib/fa";
Now that section we could use two type of method.
First one :-
Here we import the all icons and use the function to get specific icon which we want
const Icon = props => {
const { iconName, size, color } = props;
const icon = React.createElement(FontAwesome[iconName]);
return <div style={{ fontSize: size, color: color }}>{icon}</div>;
};
const App = () => {
const iconString = "FaBeer";
const beer = React.createElement(FontAwesome[iconString]);
return (
<div>
<Icon iconName={"FaBeer"} size={12} color="orange" />
</div>
);
};
render(<App />, document.getElementById("root"));
And Second :-
const App = () => {
const iconString = "FaBeer";
const beer = React.createElement(FontAwesome[iconString]);
return (
<div>
<FontAwesome.FaBeer />
<div style={{ fontSize: 24, color: "orange" }}>{beer}</div>
</div>
);
};
render(<App />, document.getElementById("root"));
Here is the Demo:- Codesandbox.
Thank to〈Evie.Codes〉.
It seems that the current answers already addresses the problem, so this only attempts to add small improvement for the solution. Here is an approach I tried in a similar situation.
Simplified demo on: stackblitz
This will keep data the same as posted in the question as it might need to be fetched:
const data = [
{
contact: [
{
title: 'contact',
icons: 'FaBeer',
},
{
title: 'contact',
icons: 'Fa500Px',
},
{
title: 'contact',
icons: 'FaChrome',
},
],
},
];
Define an object with the imported icons as static data:
import { FaBeer, Fa500Px, FaChrome } from 'react-icons/fa';
const icons = { FaBeer, Fa500Px, FaChrome };
In the output, the icon can taken out from static data, and rendered on condition:
const Contact = () => {
return (
<>
{data.map((e, i) => (
<React.Fragment key={i}>
{e.contact.map((item, index) => {
const Icon = icons?.[item.icons];
return (
<div key={index} className="text-area">
<span>{Icon && <Icon size="3em" color="hotpink" />}</span>
</div>
);
})}
</React.Fragment>
))}
</>
);
};
export default contact;
import { FaBeer, Fa500Px, FeAccusoft } from "react-icons/fa";
note: there is a typo in the name of the icon you imported .. it should be FaAccusoft
my suggestion for your question is to store the Icon components themselves in the object property .. so instead of storing it as string: "FaBeer" ... store it as a component: <FaBeer /> directly inside the object property .. like this:
const data = [
{
contact: [
{
title: "contact-1",
icons: <FaBeer />
},
{
title: "contact-2",
icons: <Fa500Px />
},
{
title: "contact-3",
icons: <FaAccusoft />
}
]
}
];
and then you can simply loop over them
const Contact = () => {
return (
<>
{data.map((e, i) => {
return (
<>
{e.contact.map((c) => {
return (
<div className="text-area">
{c.title}
<span>
{c.icons} // you simply call it like this and the icon will be rendered
</span>
</div>
);
})}
</>
);
})}
</>
);
};
You can also use Parser() from html-react-parser. https://github.com/remarkablemark/html-react-parser
const parse = require('html-react-parser');
{parse(`<${e.contact.icons}/>`)};

antd menu generated dynmically from array leads to duplicated key 'undefined' used in Menu by path []

My code is pretty straightforward but leads to the following warning in the console:
index.js:1 Warning: Duplicated key 'undefined' used in Menu by path []
This is so odd since all the relevant elements do have unique keys...?!
import { useState } from 'react';
import {
Layout,
Menu
} from 'antd';
import './navigation.less';
const { Sider } = Layout;
const items = [
{
id: 'abc',
title: "MenuItem 1",
navKey: 1,
disabled: false
},
{
id: 'def',
title: "MenuItem 2",
navKey: 2,
disabled: false
},
{
id: 'ghj',
title: "MenuItem 3",
navKey: 3,
disabled: true
},
]
const MenuItem = ({navKey, title, disabled}) => {
return (
<Menu.Item key={navKey} disabled={disabled}>
{title}
</Menu.Item>
)
}
const menu = items.map((item) => <MenuItem key={item.id} {...item} />);
const Navigation = () => {
const [collapsed, setCollapsed] = useState(false);
return (
<Sider collapsible collapsed={collapsed} onCollapse={() => setCollapsed(!collapsed)} width={260}>
<Menu theme="light" mode="inline">
<Menu.Divider />
{menu}
<Menu.Divider />
</Menu>
</Sider>
);
}
export default Navigation;
Your menu syntax is wrong.
The change below will get it to work.
const menu = items.map((item) => MenuItem(item))

React component from constant

I have js file with routes for my menu.
menuConstant.js
import PeopleIcon from '#material-ui/icons/People'
export const routers = [
{
title: 'Clients',
to: '/clients',
icon: PeopleIcon, // doesn't work
items: [
{ itemTitle: 'Invoice', itemTo: '/invoice' },
{ itemTitle: 'Test', itemTo: '/test' }
]
},
{ title: 'systems', to: '/systems' },
{ title: 'system accesses', to: '/system-accesses' }
]
I'am using material ui List and ListItem for render menu. How can i add material ui Icons in constant file that will render in menu?
Menu.jsx
export const ResponsiveDrawerItem = ({ className, title, to, icon }) => {
return (
<ListItem className={className} component={NavLinkRouter} button activeClassName='Mui-selected' exact to={to}>
<ListItemIcon>{icon}</ListItemIcon> // icon from constant not working
<ListItemText primary={title} />
</ListItem>
)
}
your material icon should be called like a component, and you should rename to titlecase icon as well:
export const ResponsiveDrawerItem = ({ className, title, to, icon: Icon }) => {
return (
<ListItem className={className} component={NavLinkRouter} button activeClassName='Mui-selected' exact to={to}>
<ListItemIcon><Icon /></ListItemIcon>
<ListItemText primary={title} />
</ListItem>
)
}

How to Add a Style Object to Each Icon in the Array? (React/Material-ui)

I'm using material-ui icons, and I would like to add a different icon for each array object shown below. Without manually adding a style property to each object icon in the array, how do I add the "iconStyle" to each object in the array?
import Icon1 from "material-ui/icons"
import Icon2 from "material-ui/icons"
//if i have an array like this:
const array = [
{name: "test1", icon: <Icon1/>}, //material ui icon1
{name: "test2", icon: <Icon2/>}, //material ui icon2
]
//and i map through it like this:
const txtStyle = {//css};
const iconStyle = {//css}
<div>
{array.map(arr => {
return <div>
<p style={txtStyle}>{arr.name}</p>
{arr.icon} // how do i add the iconStyle to this element?
</div>
})}
</div>
// how do i add the iconStyle object to the person.icon without adding it manually to each icon in the array?
//not like this:
const array = [
{name: "test1", icon: <Icon1 style={iconStyle}/>}, //material ui icon1
{name: "test2", icon: <Icon2 style={iconStyle}/>}, //material ui icon2
]
Any feedback/help would be appreciated!
there are two ways to do that.
Method 1
Make your icons accepts the props
const arr = [
{ name: "Circle", Icon: (props) => <Icon1 {...props} /> },
{ name: "Shopping", Icon: (props) => <Icon2 {...props} /> }
];
Now the style object can be passed as a regular prop to the icons.
<div>
{arr.map((item) => (
<>
<p style={txtStyle}>{item.name}</p>
<item.Icon style={iconStyle}/>
</>
))}
</div>
Method 2
If you're using makeStyles to add the styles then you can add a class to the container div and then target the icon from that container rule.
export default function App() {
const classes = useStyles();
return (
<Box>
{arr1.map((item) => (
<div className={classes.iconDiv}>
<p>{item.name}</p>
{item.icon}
</div>
))}
</Box>
);
}
const useStyles = makeStyles((theme) => ({
iconDiv: {
"&>p": {
color: "red"
},
"&>svg": {
color: "green",
cursor: "pointer"
}
}
}));
in this method, there's no need to change the arr array.
Here is a working example of both methods...
Just you can take only reference of the icon component then render it in array.map
const array = [
{name: "test1", icon: Icon1}, // attention on this line
{name: "test2", icon: Icon2},
]
const txtStyle = {};
const iconStyle = {};
return (
<div>
{array.map(arr => (
<div>
<p style={txtStyle}>{arr.name}</p>
<arr.icon style={iconStyle}/>
</div>
)}
</div>
)
import Icon1 from "material-ui/icons"
import Icon2 from "material-ui/icons"
import { SvgIcon } from "#material-ui/core"; //import SvgIcon
const array = [
{name: "test1", icon: Icon1}, //remove tags
{name: "test2", icon: Icon2},
]
<div>
{array.map(arr => { //use component and svg icon
return <div>
<p style={txtStyle}>{arr.name}</p>
<SvgIcon component={arr.icon} className="whateverclassyouwant"/>
</div>
})}
</div>

Resources