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