3

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 ?
DSDmark
  • 1,045
  • 5
  • 11
  • 25

7 Answers7

3

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}/>.

Linda Paiste
  • 38,446
  • 6
  • 64
  • 102
Agney
  • 18,522
  • 7
  • 57
  • 75
3

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;

Sojib
  • 76
  • 1
  • 3
  • I'm honestly surprised that `` works as I thought that a [capitalized name](https://reactjs.org/docs/jsx-in-depth.html#user-defined-components-must-be-capitalized) was a strict requirement. Possibly it's ok because the actual value of `e.icons` is a component with a capitalized name? – Linda Paiste Dec 25 '22 at 18:01
2

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

Uladzimir
  • 500
  • 2
  • 5
1

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〉.

DSDmark
  • 1,045
  • 5
  • 11
  • 25
  • 4
    but this way do you think it is good for memory to import the whole ```FontAwesome``` package into this function scope? or it is better to import just required icons. – Ali Shefaee Jul 17 '22 at 16:17
1

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;
John Li
  • 6,976
  • 3
  • 3
  • 27
  • 1
    Thank you for your feedback, Mr.@.John Li. But, it is a little modification. It not resolves the current issue. In your answer. We need to traverse the entire `icons?.[item.icons];` it's depending up on the `icons`. As you know, `icons` is the object which is the collection of few elements `{ FaBeer, Fa500Px, FaChrome };`. If I change in my `data` object and add some more icons or remove some icons. It will cause some error. The only option as I can see is to import all the icons from `react-icons/fa` but. It will decrease the performance when project grows large and not good for memory. – DSDmark Dec 28 '22 at 17:48
1

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>
              );
            })}
          </>
        );
      })}
    </>
  );
};
Raafat dev
  • 291
  • 1
  • 7
  • Your help is appreciable @Raafat dev. But, the things are, I have no plan to make data static. It will be dynamic. I fetch from server, So, As you guess. Your answer is very obvious. I already try that but, it’s my helplessness to convert data into string. My question is only an example. As you know, the data is in array in object or JSON form. So, the most of the case, i not able to render elements as components. – DSDmark Dec 31 '22 at 06:56
0

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}/>`)};
Santosh Karanam
  • 1,077
  • 11
  • 23
  • did you add - import { FaBeer, Fa500Px, FaAccusoft } from "react-icons/fa"; in your file – Santosh Karanam Jan 14 '22 at 12:49
  • yes, let me create https://codesandbox.io for you, give some time – Santosh Karanam Jan 14 '22 at 13:01
  • the npm package has an issue with capital case elements, { FaBeer, Fa500Px, FaAccusoft } https://github.com/remarkablemark/html-react-parser/issues/62 I can see in the browser the elements are parsed and rendered with lowercase like fabeer,fa500px,faaccusoft and browser does not recognize them. the lib says to add { htmlparser2: { lowerCaseTags: false } } option, but still it doesnot work according to git issue. ill see if there is any other lib or fix for this – Santosh Karanam Jan 14 '22 at 13:45