3

I have a sidebar components whose list items are generated inside of a map function (movie genres fetched from an api). I would like to have a different icon next to each of the movie genres but have been unable to figure out a way to create a different for each run of the map function.

My thoughts are 1st: make an array of the icons I would like to display, in the order that I would like them to display in.

 const icons = [
    "AiOutlineHome ",
    "FaUserNinja",
    "GiSwordman",
    "GiBabyFace",
    "FaLaughBeam",
    "GiPistolGun",
    "GiPineTree",
    "GiDramaMasks",
    "GiFamilyHouse",
    "GiElfEar",
    "GiScrollUnfurled",
    "GiScreaming",
    "GiMusicalNotes",
    "GiMagnifyingGlass",
    "FaRegKissBeam",
    "GiMaterialsScience",
    "GiHalfDead",
    "GiGreatWarTank",
    "GiCowboyBoot",
  ];

Then in my map function generate a different icon for each list item

{movieGenres.map((genre) => {
            return (
              <li key={genre.id} className="nav-text">
                <button
                  className="genre-btn"
                  onClick={() => genreSelectionHandler(genre.id)}
                >
                  *<GENERATE ICON HERE />*
                  <span className="nav-item-title">{genre.name}</span>
                </button>
              </li>
            );
          })}

Is it possible to pragmatically create components like this? So that I can avoid creating each list item individually if I want different icon.

yaboi-hugh
  • 163
  • 3
  • 10
  • What format are your icons? You could have a generic component called ```Icon``` that will take a path for the image to show as the icon. Your mapping would look slightly different though. For each icon name, you would have to keep track of the path to it. So it could be turn to an object instead – szczocik Oct 09 '20 at 09:51

1 Answers1

8

You can just take the index of your genre to get the icon. The index is the second argument to the callback of your map() function:

{movieGenres.map((genre, idx) => (
    <li key={genre.id} className="nav-text">
        <button
            className="genre-btn"
            onClick={() => genreSelectionHandler(genre.id)}
        >
            <Icon icon={icons[idx]} />
            <span className="nav-item-title">{genre.name}</span>
        </button>
    </li>
))}

The Icon component would render the icon depending on what kind of icons you are using.

EDIT:

As you want to use react-icons you need to import the actual icon components rather than just using strings:

import {AiOutlineHome} from "react-icons/ai";
import {FaUserNinja} from "react-icons/fa";
// ....

const icons = [
    AiOutlineHome,
    FaUserNinja,
    // ...
];

And render them:

{movieGenres.map((genre, idx) => {
    // must be a capitalized name in order for react to treat it as a component
    const Icon = icons[idx];

    return (
        <li key={genre.id} className="nav-text">
            <button
                className="genre-btn"
                onClick={() => genreSelectionHandler(genre.id)}
            >
                <Icon />
                <span className="nav-item-title">{genre.name}</span>
            </button>
        </li>
    )
})}
trixn
  • 15,761
  • 2
  • 38
  • 55
  • Thank you for your answer, what is the Icon component? where is it imported from? – yaboi-hugh Oct 09 '20 at 10:48
  • @user109949 You would have to write it yourself or replace it with whatever your icon element looks like. You did not provide any information about how you want to render the actual icons. – trixn Oct 09 '20 at 10:50
  • @user109949 Okay I just saw you tagged the question with `react-icons` which I guess its the library you are using to render icons? Then you have to import the actual icon components and replace the strings in your `icons` array with the component classes. – trixn Oct 09 '20 at 10:55
  • @user109949 I updated my answer with how you would do it using `react-icons`. – trixn Oct 09 '20 at 11:04
  • @user109949 You're welcome. But keep in mind that if your `genres` list exeeds the length of the `icons` list you will need to handle that, e.g. with a fallback Icon or conditional rendering. – trixn Oct 09 '20 at 11:09