Using react-icons that are imported from another file - reactjs

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

Related

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}/>`)};

TypeError: Cannot read property '0' of undefined when building my react app

while building my react app for deployment, I am getting this error
TypeError: Cannot read property '0' of undefined
when I am rending on port3000 I did not see this error but only get it while building the app.
Can anyone assist to resolve this?
import { useState } from "react";
import styles from "./Tabs.module.css"
const Tabs = ({ children}) => {
const [activeTab, setActiveTab] = useState (children [0].props.label);
const handleClick =( e, newActiveTab ) => {
e.preventDefault();
setActiveTab(newActiveTab);
}
return (
<div>
<ul className= {styles.tabs}>
{children.map ((tab) => {
const label = tab.props.label;
return (
<li
className= {label == activeTab ? styles.current : ""}
key= {label}
>
<a href="#" onClick={(e) => handleClick (e, label)}>{label}
</a>
</li>
)
})}
</ul>
{children.map ((tabcontent1) => {
if (tabcontent1.props.label == activeTab)
return (
<div key= {tabcontent1.props.label} className= {styles.content}>{tabcontent1.props.children}
</div>
);
})}
</div>
);
}
export default Tabs ;
In next js, when you don't put export const getServerSideProps = () => {} in your page then that page is automatically subjected to static side rendering. On development mode, you may see a lightening symbol on bottom-right. Anyway you can read the docs on data-fetching on nextjs. However, your issue on this situation can be easily fixed by setting the children through useEffect.
// handle null on your active tab render function
const [activeTab, setActiveTab] = useState(null);
useEffect(() => {
if(children.length)
children[0].props.label
}, [children])
Another Code Sample:
*A simple change in code structure and the way you are trying to do. It's on react but kind of same in next as well *
import React from "react";
const Tabs = ({ tabsData }) => {
const [activeTabIndex, setActiveTabIndex] = React.useState(0);
const switchTabs = (index) => setActiveTabIndex(index);
return (
<div style={{ display: "flex", gap: 20, cursor: "pointer" }}>
{/* Here active tab is given a green color and non actives grey */}
{tabsData.map((x, i) => (
<div
key={i}
style={{ color: activeTabIndex === i ? "green" : "#bbb" }}
onClick={() => switchTabs(i)}
>
{x.label}
</div>
))}
{/* Show Active Tab Content */}
{tabsData[activeTabIndex].content}
</div>
);
};
export default function App() {
// You can place it inside tabs also in this case
// but lets say you have some states on this component
const tabsData = React.useMemo(() => {
return [
// content can be any component or React Element
{ label: "Profile", content: <p>Verify all Input</p> },
{ label: "Settings", content: <p>Settings Input</p> },
{ label: "Info", content: <p>INput info</p> }
];
}, []);
return (
<div className="App">
<Tabs tabsData={tabsData} />
</div>
);
}
and here is also a example sandbox https://codesandbox.io/s/serverless-night-ufqr5?file=/src/App.js:0-1219

Add an object to nested array in React-Recoil

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>
);
}

Components do not change when a hooked element is Changed

Currently, the details of the elements which I want to display are saved at Info.js.
Parent.js is responsible for importing the details needed and then injecting them respectively into each Child.js by .map function as the info stored at Info.js is an array.
I want to dynamically display the relative Child component by the button pressed by users. For example, when the user clicked "First-Tier" button at Parent.js, only the Child.js component with the category of "First-Tier" will be shown. At this moment, my code is not working. I believe the problem is at useEffect but I cannot figure out how to fix this.
I am looking forward to receiving your inspirations. Thanks and please stay safe.
---> Parent.js
import React, { useState, useEffect } from "react";
import Info from "./Info";
import Child from "./Child";
let Category = ["All", "First-Tier", "Second-Tier"];
const Parent = () => {
const [categoryChosen, setCategoryChosen] = useState("All");
let PartsShown = [...Info];
useEffect(() => {
PartsShown = [
...PartsShown.filter((e) => e.category[1] === categoryChosen),
];
}, [categoryChosen, PartsShown]);
return (
<div>
<div>
{Category.map((element) => (
<button
style={{ margin: 10 }}
key={element}
onClick={() => setCategoryChosen(element)}
>
{element}
</button>
))}
</div>
<div>{categoryChosen}</div>
<div>
{PartsShown.map((e) => (
<Child
key={e.name}
name={e.name}
category={e.category[1]}
/>
))}
</div>
</div>
);
};
export default Parent;
---> Child.js
import React from "react";
const Child = ({ name, category }) => (
<div style={{ margin: 10 }}>
<h1>{name}</h1>
<p>{category}</p>
<hr />
</div>
);
export default Child;
--> Info.js
const Info = [
{
name: "A",
description: "Description of A ",
category: ["All", "First-Tier"],
},
{
name: "B",
description: "Description of B",
category: ["All", "Second-Tier"],
}
];
export default Info;
import React, { useState } from "react";
import Info from "./Info";
import Child from "./Child";
const Category = ["All", "First-Tier", "Second-Tier"];
const Parent = () => {
const [partsShown, setPartsShownAndCategory] = useState({
partsArray: [...Info],
category: "All"
});
const changeCategory = category => {
const PartsShown = Info.filter(
element =>
element.category[1] === category || element.category[0] === category
);
setPartsShownAndCategory({
...partsShown,
category: category,
partsArray: PartsShown
});
};
console.log(partsShown);
return (
<div>
<div>
{Category.map(element => (
<button
style={{ margin: 10 }}
key={element}
onClick={() => changeCategory(element)}
>
{element}
</button>
))}
</div>
<div>{partsShown.category}</div>
<div>
{partsShown.partsArray.map(e => (
<Child key={e.name} name={e.name} category={partsShown.category} />
))}
</div>
</div>
);
};
export default Parent;

How to manipulate style/atributes in React DOM

I have two components. If I hovered over one I'd like to move (by changing style proporties) the other one component.
How can I achieve that?
In pure js it's simply
let elem1 = document.querySelector('.elem1');
let elem2 = document.querySelector('.elem2');
elemt1.addEventListener('mouseover', () => {
elem2.style.right = "200px" //or any other style property
})
So.. in react we can use "ref" and this works if I define static ref
import React, {Component} from 'react';
class MainCanvas extends Component {
onHover(){
console.log(this.refs.mybtntest);
}
render(){
return(
<div>
<h1 onMouseEnter={() => this.onHover()}> Testing</h1>
<button ref="mybtntest">Close</button>
</div>
);
}
}
export default MainCanvas
However in my case I need to each component should has dynamically added "ref" atribute.. so my code looks like below
import React, {Component} from 'react';
class Test extends Component {
onHover(e, dynamicRef){
console.log(dynamicRef); //correct number of ref
console.log(this.refs.dynamicRef); //undefined
console.log(this.refBtnName); //button reference but eachtime is overrided
console.log(this.dynamicRef);//undefinded
}
render(){
const elements = this.props.elements.map( element => {
let refBtnName = element.id + "btn";
return [
<ComponentElement
onHover={(e) => this.onHover(e, refBtnName)}
key={element.id} {...element}
/>,
<button key={element.id*2}
ref={refBtnName => this.refBtnName = refBtnName} //each time he will be overrided :(
className={`${refBtnName} deleteComponentBtn`} >
Close
</button>
]
})
return(
<div>
{elements}
</div>
);
}
}
export default Test
A real goal is that I want to positioning the button relative to the element. I could use div for this purpose as a wrapper but I don't want it.
So I thought to use for example this piece of code
onHover(e, dynamicRef){
Math.trunc(e.target.getBoundingClientRect().right)
dynamicRef.style.right = `${right}px`;
}
If you need dynamic object keys you shouldn't use the dot . and instead use the brackets:
ref={ref=> this[refBtnName] = ref}
Note that i changed the inline parameter to ref instead of refBtnName so you won't get variable names conflicts.
Running example:
class App extends React.Component {
state = {
items: [
{ name: 'John', id: 1 },
{ name: 'Mike', id: 2 },
{ name: 'Jane', id: 3 },
]
}
move = refName => e => {
this[refName].style.right = '-90px';
}
render() {
const { items } = this.state;
return (
<div>
{
items.map(item => {
return (
<div key={item.id} >
<div
ref={ref => { this[item.name] = ref }}
style={{ position: 'relative' }}
>
{item.name}
</div>
<button onClick={this.move(item.name)}>Move {item.name}</button>
</div>
)
})
}
</div>
)
}
}
ReactDOM.render(<App />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>

Resources