Search code examples
javascriptreactjsimagecomponents

Render 1 image in 3 repeated components


I'm relatively new to React JS and have encountered my first blocker.

I have a component that renders 3 pieces of information and each piece requires a different image that goes with it. See this image: too many images

This is obviously each image, rendered 3 times instead of once. It should look something like this: correct approach

The only issue is, there are 3 images and I'm not sure how to reflect each other image on each respective component if that makes sense. I've tried a couple approaches but either end up with 1 image or all 3. It makes sense as I render that component 3 times so I understand my error, but what is the best solution in this situation?

My current approach:

const images = [
    {
        id: "img1",
        source: require("../../assets/images/image-retro-pcs.jpg")
    },
    // {
    //     id: "img2",
    //     source: require("../../assets/images/image-top-laptops.jpg")
    // },
    // {
    //     id: "img3",
    //     source: require("../../assets/images/image-gaming-growth.jpg")
    // }
];

const FooterItem = (props) => {
    const imageSource = images.map((image) => 
        <img 
            src={image.source} 
            alt="" 
            className={classes.image} 
            key={image.id}
        />)

    return (
        <div className={classes.itemContainer}>
            {imageSource}
            <div className={classes.footerItems}>
                <h2 className={classes.h2}>{props.number}</h2>
                <h3 className={classes.h3}>{props.title}</h3>
                <p className={classes.p}>{props.info}</p>
            </div>
        </div>
    );
};

export default FooterItem;

So with those 2 commented out, we get 1 image in each correct component.

The other component code is:

const Footer = () => {
  return (
    <div className={classes.footer}>
        <FooterItem 
            number="01"
            title="Reviving Retro PCs"
            info="What happens when old PCs are given modern upgrades?"
        />
        <FooterItem 
            number="02"
            title="Top 10 Laptops of 2022"
            info="Our best picks for various needs and budgets."
        />
        <FooterItem 
            number="03"
            title="The Growth of Gaming"
            info="How the pandemic has sparked fresh opportunities."
        />
    </div>
  );
};

What would be the best 3 to not have 3 images rendered but instead 1? a forEach loop?

Edit: I resolved it by refactoring my code like this:

const images = [
    {
        id: "img1",
        source: require("../../assets/images/image-retro-pcs.jpg"),
        number:"01",
        title:"Reviving Retro PCs",
        info:"What happens when old PCs are given modern upgrades?"
    },
    {
        id: "img2",
        source: require("../../assets/images/image-top-laptops.jpg"),
        number:"02",
        title:"Top 10 Laptops of 2022",
        info:"Our best picks for various needs and budgets."
    },
    {
        id: "img3",
        source: require("../../assets/images/image-gaming-growth.jpg"),
        number:"03",
        title:"The Growth of Gaming",
        info:"How the pandemic has sparked fresh opportunities."
    }
];

const FooterItem = () => {
    const imageSource = images.map((image) => 
    <div className={classes.itemContainer} key={image.id}>
        <img 
            src={image.source} 
            alt="" 
            className={classes.image} 
        />
        <div className={classes.footerItems}>
                <h2 className={classes.h2}>{image.number}</h2>
                <h3 className={classes.h3}>{image.title}</h3>
                <p className={classes.p}>{image.info}</p>
        </div>
    </div>
    )

    return (
        <div className={classes.itemContainer}>
            {imageSource}
        </div>
    );
};

export default FooterItem;

OTHER FILE:
const Footer = () => {
  return (
    <div className={classes.footer}>
        <FooterItem />
    </div>
  );
};

Thanks for any help in advance.


Solution

  • You can send one more prop that will contain id of image as:

    CODESANDBOX

    <FooterItem
            number="01"
            title="Reviving Retro PCs"
            info="What happens when old PCs are given modern upgrades?"
            imageId="img1"
          />
    

    and inside the FooterItem, you can find image using imageID as:

    const image = images.find((o) => o.id === props.imageId);
    

    and render it as:

    <div className={classes.itemContainer}>
          {image ? (
            <img
              src={image.source}
              alt=""
              className={classes.image}
              key={image.id}
            />
          ) : null}
          <div className={classes.footerItems}>
            <h2 className={classes.h2}>{props.number}</h2>
            <h3 className={classes.h3}>{props.title}</h3>
            <p className={classes.p}>{props.info}</p>
          </div>
        </div>
    

    To improve your code more then you can create an array of objects that contain all footer data and then render it as:

    CODESANDBOX

    const Footer = () => {
      const footerData = [
        {
          number: "01",
          title: "Reviving Retro PCs",
          info: "What happens when old PCs are given modern upgrades?",
          imageId: "img1"
        },
        {
          number: "02",
          title: "Top 10 Laptops of 2022",
          info: "Our best picks for various needs and budgets.",
          imageId: "img2"
        },
        {
          number: "03",
          title: "The Growth of Gaming",
          info: "How the pandemic has sparked fresh opportunities.",
          imageId: "img3"
        }
      ];
      return (
        <div className={classes.footer}>
          {footerData.map((o) => {
            return (
              <FooterItem
                key={o.number}
                number={o.number}
                title={o.title}
                info={o.info}
                imageId={o.imageId}
              />
            );
          })}
        </div>
      );
    };