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}/>`)};
Related
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
Want to make a Quiz application on WordPress Gutenberg with React? The problem goes, when I try to make a repeater field of the quiz section. I use UseState to make the components Repeat the section onClick. But I can't edit the input field after repeating it.
index.js
import { registerBlockType } from "#wordpress/blocks";
import "./style.scss";
import Edit from "./edit";
import save from "./save";
import metadata from "./block.json";
registerBlockType(metadata.name, {
attributes: {
ids: { type: "array", default: 1},
question: { type: "string", default:"Example Question"},
answers: { type: "array", default: [""] },
correctAnswer: { type: "number", default: undefined },
bgColor: { type: "string", default: "#ededed" },
},
edit: Edit,
save,
});
edit.js `
import { __ } from "#wordpress/i18n";
import {
TextControl,
Flex,
FlexBlock,
FlexItem,
Button,
Icon,
} from "#wordpress/components";
import { useBlockProps } from "#wordpress/block-editor";
const { useState} = wp.element;
import "./editor.scss";
import QuizWrapper from "./components/QuizWrapper";
import TestComponent from "./components/TestComponent";
export default function Edit(props) {
const [quizwrapper, setQuizwrapper] = useState([<QuizWrapper props={props} key={0} />])
const blockProps = useBlockProps({
className: "quiz-wrapper",
});
function addSection() {
setQuizwrapper([...quizwrapper,<QuizWrapper props={props} key={quizwrapper.length} />]);
}
return (
<div {...blockProps}>
<QuizWrapper props={props}></QuizWrapper>
{quizwrapper}
<Button isPrimary onClick={addSection}>
Add Another Section
</Button>
</div>
);
}
When I use <QuizWraper /> as a component it works fine on editing and changing on Question and Answer section, image has been uploaded below
but for the UseState hook for repeating the component {quizwrapper} repeates the section but doesn't allow me to edit and change the section
Image of the editor WordPress dashboard
QuizWrapper.jsx
import React from 'react'
import Question from "./Question"
import Answers from "./Answers"
const QuizWrapper = ({props}) => {
return (
<div className='quizes'>
<Question props={props}></Question>
<Answers props={props}></Answers>
</div>
)
}
export default QuizWrapper
Question.js
import { TextControl } from "#wordpress/components";
export default function Question({props}) {
function updateQuestion(value) {
props.setAttributes({ question: value });
}
return (
<div className="question">
<h2 style={{ fontSize: "20px", margin: "20px 0 8px 0" }}>Questions: </h2>
<TextControl
value={props.attributes.question}
onChange={updateQuestion}
/>
</div>
);
}
Answer.js
import { TextControl, Button, Icon } from "#wordpress/components";
export default function Answers({ ids, props }) {
function markasCorrect(index) {
props.setAttributes({ correctAnswer: index });
}
function deleteAnswer(indextoDelete) {
const newAnswer = props.attributes.answers.filter(function (x, index) {
return index != indextoDelete;
});
props.setAttributes({ answers: newAnswer });
if (indextoDelete == props.attributes.correctAnswer) {
props.setAttributes({ correctAnswer: undefined });
}
}
return (
<div className="answers" key={ids}>
<p style={{ fontSize: "13px", margin: "20px 0 8px 0" }}>Answers: </p>
{props.attributes.answers.map((answer, index) => {
return (
<div className="answer-wrapper">
<Button onClick={() => markasCorrect(index)}>
<Icon
className="mark-as-correct"
icon={
props.attributes.correctAnswer == index ? "yes-alt" : "yes"
}
/>
</Button>
<TextControl
value={answer}
onChange={(updateanswer) => {
const newAnswers = props.attributes.answers.concat([]);
newAnswers[index] = updateanswer;
props.setAttributes({ answers: newAnswers });
}}
></TextControl>
<Button
isLink
className="deleteBtn"
onClick={() => deleteAnswer(index)}
>
Delete
</Button>
</div>
);
})}
<Button
isPrimary
onClick={() => {
props.setAttributes({
answers: props.attributes.answers.concat([""]),
});
}}
>
Add Another Answer
</Button>
</div>
);
}
Need Solution: How I can make the repeater section of the <Quizwapper>component and how I can make the repeater section where the <Question/> and <Answer/> components will work fine.
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}
</>
))}
</>
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>
I am trying to update an object to the nested array
and the code below works well when I tried with React Hooks, using useState to update the state.
Then I tried to add a state management library named Recoil, a new library from Facebook, which is super simple and behaves like Hooks.
But after I changed my code adding some Recoil codes, like the atom, useRecoilState, adding a new object to the nested array stopped working.
I tried a few different methods to solve this and find other solutions but didn't go well.
Does anyone know how to solve this? You can check the code and codesandbox link below.
https://codesandbox.io/s/nested-arrays-qrw95?file=/src/App.js:0-1547
import React from "react";
import { RecoilRoot, atom, useRecoilState } from "recoil";
const todoListState = atom({
key: "TodoList",
default: [
{
name: "Lee",
device: [
{ name: "MBPR", price: 3000 },
{ name: "MBA", price: 2000 }
]
}
]
});
export function TodoApp() {
const [list, setList] = useRecoilState(todoListState);
return (
<>
{list.map((x, i) => {
return (
<div style={{ border: "1px solid black", textAlign: "left" }} key={i}>
<div>{x.name}</div>
<div>Devices</div>
<ul>
{x.device.map((y, j) => {
return (
<>
<li key={j}>{y.name}</li>
</>
);
})}
</ul>
<button
onClick={() => {
const clone = [...list];
clone[i].device = [...clone[i].device, { name: "IPX" }];
setList(clone);
}}
>
Add List
</button>
</div>
);
})}
<button
onClick={() => {
const newData = { name: "Kristoffer", device: [{ name: "Nokia" }] };
setList([...list, newData]);
}}
>
Add Item
</button>
</>
);
}
export default function App() {
return (
<RecoilRoot>
<div className="App">
<TodoApp />
</div>
</RecoilRoot>
);
}