How to map React Icons in react - reactjs

I have the icon imports above
import { MdSettings } from "react-icons/md";
import { RiAccountPinCircleFill } from "react-icons/ri";
import { BsSunFill } from "react-icons/bs";
then here is the array containing the imports
const Icons = ["MdSettings", "RiAccountPinCircleFill", "BsSunFill"];
I want to map it here
<div className="topbar-links inline-flex">
<ul>*this is where I want it rendered*</ul>
</div>

First of all, if your variable is not a component, you should avoid to use PascalCase for the name. So you can rename Icons as icons.
Then, you should store the elements imported directly in the array and not just their names as string, or you will not be able to use the components:
const icons = [MdSettings, RiAccountPinCircleFill, BsSunFill];
Finally, for the loop you can do like this:
<div className="topbar-links inline-flex">
<ul>
{icons.map((Icon, i) => (
<li key={i}>
<Icon />
</li>
))}
</ul>
</div>

If the icons are react components you can store the icons as
const Icons = [<MdSettings/>, <RiAccountPinCircleFill/>, <BsSunFill/>]
then you can map them as
<div className="topbar-links inline-flex">
<ul>{Icons.map((icon, index) => <li key={index}>{icon}</li>}</ul>
</div>

Related

React: How to display react-icons element using fetch API?

I stored my social links data on firebase, and now I want to make a <li> list of my social links but the icon object I have here, is not showing me the icons instead it's showing me the elements like: <BsBehance />. It should be displayed as icon, how can I do that?
Firebase data:-
Code:-
import { useEffect, useState } from "react";
import * as ReactIcons from "react-icons/fa";
import MainWrapper from "./MainWrapper";
import classes from "./pages.module.css";
export default function Footer() {
const [socialLinks, setSocialLinks] = useState([]);
useEffect(() => {
const fetchSocilLinks = async () => {
const res = await fetch(
"https://mohitdevelops-d64e5-default-rtdb.asia-southeast1.firebasedatabase.app/accounts.json"
);
const data = await res.json();
let loadedData = [];
for (const keys in data) {
loadedData.push({
url: data[keys].url,
icon: data[keys].icon,
id: data[keys].id,
});
}
setSocialLinks(loadedData);
};
fetchSocilLinks().catch((err) => {
console.log(err.message);
});
}, [socialLinks]);
console.log(socialLinks);
return (
<footer className={classes.footer__wrap}>
<MainWrapper>
<p>Connect with me:</p>
<ul className={classes.social_links}>
{socialLinks?.map(({ icon, id, url }) => {
const iconLink = icon.split(/\<|\/>/).filter((e) => e)[0];
const IconsComponent = ReactIcons[iconLink];
return (
<li key={id}>
<a href={url} target="_blank">
<IconsComponent />
</a>
</li>
);
})}
</ul>
</MainWrapper>
</footer>
);
}
And its showing me like this:-
Everything data is coming fine, just need to know how to display an element which is working like an icon.
Since el.icon is a string, it's being displayed as a string. If you want to render the component based on that dynamic string, you need to have an object, where the key name is the string you will get from the server. And the value, can be the component itself.
When you insert a string inside {}, React will render it as a string, even if they're in the < /> format.
And also, try to never use dangerouslySetInnerHTML, since it has high security issues.
Here is a codesanbox, for your above case. Please check this:
You need to use dangerouslySetInnerHTML like this
<ul className={classes.social_links}>
{socialLinks.map((el) => {
return (
<li key={el.id}>
<a href={el.url} target="_blank">
<span dangerouslySetInnerHTML={{__html: el.icon}} />
</a>
</li>
);
})}
</ul>
The icon which need to display should be import from the react-icon/bs and that icon name is coming from the firebase data, so it can be import using dynamic import in react.js but for dynamic import you need to handle loading separately, but I have provided easy way.
Need to install react-icon
npm install react-icons --save
And then import all the Icon
import * as ReactIcons from "react-icons/bs";
And add this line of code
<ul>
{socialLinks?.map(({ icon, id, url }) => {
const iconLink = icon.split(/\<|\/>/).filter((e) => e)[0];
const Compoenent = ReactIcons[iconLink];
return (
<li key={id}>
<a href={url}>
<Compoenent />
</a>
</li>
);
})}
</ul>

How to update image source on hover?

I'm trying to use Next.js Image component to render images on my app page. I'm having issues understanding how to select and update the main Image src so that I can replace it with all the responsive sizes Next.js creates for Image elements.
I have a list of navigation links in my app menu and I want to assign data attributes to each one so that when these links are hovered over they update the main Image element and display a different main image for each link hovered over.
I'm new to React and the way it works, so I'm not sure what my issues are but I have made a start with some basic concepts. I have started to console log the data I have to see what I get but now I've hit a brick wall.
Here is what I have so far:
import React, { useState, useEffect, useRef } from 'react';
import Image from 'next/image';
function Header() {
const MenuHeroImg = useRef();
function handleMouseEnter(e) {
console.log(e.target.getAttribute('data-project-image-url'));
console.log(MenuHeroImg.current);
}
return (
<>
<ul>
<li
onMouseEnter={handleMouseEnter}
data-project-image-url="/public/images/projects/image_2.png"
className={`${navigationStyles['c-navigation-menu-link']}`}>
<Link href="/project-link-here">Link</Link>
</li>
<li
onMouseEnter={handleMouseEnter}
data-project-image-url="/public/images/projects/image_3.png"
className={`${navigationStyles['c-navigation-menu-link']}`}>
<Link href="/project-link-here_2">Link</Link>
</li>
<ul>
<picture ref={MenuHeroImg}>
<Image
src="/public/images/projects/image_1.png"
alt="Image"
width={660}
height={835}
layout="responsive"
/>
</picture>
</>
);
}
export default Header;
Console Log:
Rather than using data-* attributes to set the images paths, move the image source into a state variable that you can then update when each onMouseEnter gets triggered.
Also note that images in the public folder are referenced as /images/projects/image_1.png, without the /public.
import React, { useState } from 'react';
import Image from 'next/image';
function Header() {
const [image, setImage] = useState('/images/projects/image_1.png');
function handleMouseEnter(imagePath) {
return () => {
setImage(imagePath);
};
}
return (
<>
<ul>
<li
onMouseEnter={handleMouseEnter('/images/projects/image_2.png')}
className={`${navigationStyles['c-navigation-menu-link']}`}
>
<Link href="/project-link-here">Link</Link>
</li>
<li
onMouseEnter={handleMouseEnter('/images/projects/image_3.png')}
className={`${navigationStyles['c-navigation-menu-link']}`}
>
<Link href="/project-link-here_2">Link</Link>
</li>
<ul>
<Image
src={image}
alt="Image"
width={660}
height={835}
layout="responsive"
/>
</>
);
}
export default Header;
the src should be like this:/images/projects/image_1.png;
because nextjs look for the image in the root folder that is the public folder

Working with images local files in Gatsby.js

I am trying to render a headshot for each artist displayed on the page. The artist data comes from a Json file, and the images are located in images/artists/headshots. I am using the regular img tag, but for some reason nothing is displaying on the page. Any help any one can give would be greatly appreciated.
import React from 'react'
import PropTypes from 'prop-types'
import { StaticImage } from 'gatsby-plugin-image'
import { useStyles } from './styles'
const ArtistsPage = ({ data }) => {
const classes = useStyles()
return (
<section>
<article className={classes.artistsBackground}>
<div className={classes.heroLayoutContainer}>
<h3 className={classes.title}>MEET THE ARTISTS</h3>
<StaticImage
className={classes.heroImage}
src='../../images/artists/hero-images/hero-image-artists.jpg'
alt='hero-image-artists'
placeholder='blurred'
/>
</div>
</article>
<article className={classes.artistsContainer}>
<div className={classes.flexContainer}>
{data.allArtistsJson.edges
.map(({ node }, idx) => {
const artist = `${node.firstName}+${node.lastName}`
.split('+')
.join(' ')
return (
<div className={classes.flexItem} key={idx}>
<div>
<img
src={`../../images/artists/headshots/${artist} Headshot.jpg`}
alt='artist-headshot'
/>
</div>
<div className={classes.artistCardName}>
{`${node.firstName} ${node.lastName}`.toUpperCase()}
</div>
<div className={classes.artistCardText}>{node.city}</div>
<div className={classes.artistCardText}>
{node.currentTeam}
</div>
</div>
)
})}
</div>
</article>
</section>
)
}
export default ArtistsPage
My image files are set up as:
FirstName LastName Headshots.jpg
I think your issue may comes from the white spaces in the naming. Your code looks good at first sight so try renaming your images with underscore or in camelCase:
<img
src={`../../images/artists/headshots/${artist}_Headshot.jpg`}
alt='artist-headshot'
/>
After much research and the nice people on Gatsby discord I found the answer to be… in a scenario like this I needed to add require().default.
Ex:
<img
src={require(../../images/artists/headshots/${artist} Headshot.jpg).default}
alt='artist-headshot'
/>

Why local svg icons not resolve in react.js projects?

I have local icons, and I add icons in build folder like screenshot below, then I was import icons like that import {ReactComponent as MyIcon} from "build/icons/my-icon.svg";, but still say "Can't resolve 'build/icons/my-icon.svg'", any idea?
Screenshot:
you need to use file-loader
and when you import dont use brackets since its default export just chose the name directly
import myIcon from "build/icons/my-icon.svg";
const App = () => {
return (
<div className="App">
<img src={myIcon} />
</div>
);
}
Svg tag
second option would be to extract the svg tag and put it directly into you component ,
const App = () => {
return (
<div className="App">
<svg .... // copy the content of your .svg
</svg>
</div>
);
}

React - Importing the JSON object only once

The following simple React component is importing a JSON file (data.js) as an object and list the items inside it.
List.js
import React from 'react'
import jsonResponse from './data'
function ZooList ({ setID }) {
const setURL = (e) => {
window.history.pushState(null, null, '/' + e)
setID(e)
}
const list = jsonResponse.animals.map((item) => {
return (
<li key={item.id}>
<div>
<img
src={item.image_url}
alt={item.name}
onClick={() => setID(item.id)}
/>
<h3>{item.name}</h3>
<p>
<b>Distribution</b>: {item.distribution}
</p>
<button onClick={() => setURL(item.id)}>More...</button>
</div>
</li>
)
})
return (
<ul>
{list}
</ul>
)
}
export default List
Now in the above page, if you click on button "More...", it calls another React component called Tile.js as fallow:
Tile.js
import React from 'react'
import jsonResponse from './data'
function Tile ({ setID, newID }) {
const clearURL = (e) => {
window.history.pushState(null, null, '/')
setID(null)
}
return (
<div>
<div>
<img
src={jsonResponse.animals[newID].image_url}
alt={jsonResponse.animals[newID].name}
/>
<h2>{jsonResponse.animals[newID].name}</h2>
<p><b>Distribution</b>: {jsonResponse.animals[newID].distribution}</p>
<StyledParagraph>{jsonResponse.animals[newID].details.long}</StyledParagraph>
<button onClick={() => clearURL(null)}>Back to overview</button>
</div>
</div>
)
}
export default Tile
The problem is that the second component is also importing the JSON file (data.js).
How can I avoid importing the data.js twice?
Generally, what would be a better way to write this app?
Imports are cached, so if you return directly a JSON with import jsonResponse from './data', the first component will import it, while the second will get it from import cache;
You can try for example, to export an instance of a class, change one of its property and then check that property in another component that make use of that import.
A ready-to-pick and very common usage example of that cache is the configureStore of react-boilerplate: it exports the store instance so whatever component import it will refer to the same store.

Resources