Home > Back-end >  Set loading image for each item between multiple items
Set loading image for each item between multiple items

Time:11-23

let [imageLoaded, setImageLoaded] = useState(false);

I am trying to load 25 images per page in my functional component with useState hook. For this, I have defined a state named imageLoaded and use it in my image tag's onLoad.

But here's the thing: images are actually in mapping, so 25 images should get loaded in total in first page. When I do it this way, loading.gif disappears after an image loads and therefore not visible for other images while they are loading.

I want the loading.gif to be visible until the image in a box fully loads, then disappears and image appears there. But right now, it applies it for all of the images when only just one loads.

I have tried to make a function for each onLoad of image, then setting a counter from 0 to 25, and when it reaches 25, I have tried to setImageLoaded to true but got the following error: Cannot create property 'current' on number '0'

How do I solve this?

<div className="row">
                    {shownImages.map((item) => (
                        <div className="images-col" key={item.edition}>
                            <div style={{ cursor: 'pointer' }} onClick={() => onPressImage(item.edition)}>
                                <div className="img-container">
                                    <ul key={item.edition}>
                                        <li className="imgPlaceholder">
                                            {imageLoaded ? null : <img alt="" src="images/loading.gif"></img>}
                                            <img style={imageLoaded ? {} : { display: 'none' }} onLoad={() => setImageLoaded(true)} src={"https://imagelink.com/"   item.imagelink.split("/").reverse()[0]} />
                                        </li>
                                    </ul>
                                </div>
                            </div>
                        </div>
                    ))} </div>

CodePudding user response:

You should define a array of imagesLoaded which has a position for each image to load into the page:

let [imagesLoaded, setImagesLoaded] = useState(new Array(25).fill(false));

Also, define the imageLoaded as a local function based on image index:

imageLoaded = (index) => imagesLoaded[index];

Finally, change setImageLoaded to:

setImageLoaded = (index) => {
    const newImagesLoaded = [...imagesLoaded];
    newImagesLoaded[index] = true;
    setImagesLoaded(newImagesLoaded);
}

So your code will be similar to:

<div className="row">
    {shownImages.map((item, index) => (
        <div className="images-col" key={item.edition}>
            <div style={{ cursor: 'pointer' }} onClick={() => onPressImage(item.edition)}>
                <div className="img-container">
                    <ul key={item.edition}>
                        <li className="imgPlaceholder">
                            {imageLoaded(index) ? null : <img alt="" src="images/loading.gif"></img>}
                            <img style={imageLoaded(index) ? {} : { display: 'none' }} onLoad={() => setImageLoaded(index)} src={"https://imagelink.com/"   item.imagelink.split("/").reverse()[0]} />
                         </li>
                     </ul>
                 </div>
             </div>
         </div>
     ))} 
</div>

CodePudding user response:

I suggest creating a component for your images. For example let's say you have created a component named ImageLoad. After that's how you should use it;

 // ImageLoad Component ==> ImageLoad.js
    
    import React from "react";
    
  export default function ImageLoad({
    item,
    onPressImage,
  }) {
    const [imageLoaded, setImageLoaded] = React.useState(false);
    return (
        <div className="images-col" key={item.edition}>
        <div style={{ cursor: 'pointer' }} onClick={onPressImage}>
            <div className="img-container">
                <ul>
                    <li className="imgPlaceholder">
                        {imageLoaded ? null : <img alt="" src="images/loading.gif"></img>}
                        <img style={imageLoaded ? {} : { display: 'none' }} onLoad={() => setImageLoaded(true)} src={"https://imagelink.com/"   item.imagelink.split("/").reverse()[0]} />
                    </li>
                </ul>
            </div>
        </div>
    </div>
    )
}

After you have successfully created your component. Then you can use it in map function like this;

<div className="row">
  {shownImages.map((item) => (
    <ImageLoad
      key={item.edition}
      item={item}
      onPressImage={onPressImage}
    />
  ))}
</div>;

This will make sure imageLoaded state will be applied specifically to each component without interfering others

  • Related