arrmani88
arrmani88

Reputation: 1068

How to wait for the images to be loaded in React

First of all I'm using ReactJS and I have the following simplified code snippet:

const Page = () => {
    const [isLoading, setIsLoading] = useState(true);
    
    return (
        <Loading isLoading={isLoading} >
            <img src='https://example.com/img1' alt='image' />
            <img src='https://example.com/img2' alt='image' />
            <img src='https://example.com/img3' alt='image' />
            <img src='https://example.com/img4' alt='image' />
            <img src='https://example.com/img5' alt='image' />
            <img src='https://example.com/img6' alt='image' />
            <img src='https://example.com/img7' alt='image' />
        </Loading>
    )
}

The problem:
The images are displayed progressively, and one after one, which doesn't look good, like the following example:

enter image description here

The solution that I'm searching for:
I wanna show the <Loading> component by setting the variable isLoading to true after finishing the loading of the images.
So when the page is opened, first the user sees a loading page, then after loading all the images, the variable isLoading is set to false, to hide the <Loading> component.

Upvotes: 1

Views: 2017

Answers (2)

cavpollo
cavpollo

Reputation: 4308

I was looking for the same solution, but I also wanted to be able to show a placeholder and something in case of an error.

In the end I settled for showing a placeholder image that would load almost instantly, and then replace it with the real deal once it is loaded:

import { useState } from 'react'

const LOADING_PREVIEW_IMAGE_PATH = '/pics/loading-preview-image.png'
const FAILED_LOAD_IMAGE_PATH = '/pics/failed-load-image.png'

function Image(realImageUrl) {
    const [isPreviewImageLoadedState, setPreviewImageLoaded] = 
        useState({image: LOADING_PREVIEW_IMAGE_PATH, error: false})

    return (
        <img src={isPreviewImageLoadedState.image}
            onLoad={() => {
                // Need to check if there was an error, or it will enter a loop where 
                // the empty image will oscillates between being loaded and the error image
                if (!isPreviewImageLoadedState.error) {
                    setPreviewImageLoaded({image: realImageUrl, error: false})
                }
            }}
            onError={() => setPreviewImageLoaded({image: FAILED_LOAD_IMAGE_PATH, error: true})} 
        />
    )
}

You may have to tweak a thing or two if you want to replace the image with different one (like setting the key={} to a Component up the chain). Just began learning ReactJS about 1 or 2 weeks ago, so take this suggestion with a pinch of salt.

Upvotes: 0

Bikas Lin
Bikas Lin

Reputation: 730

You can use onLoad attribute for your purpose.

const Page = () => {
    const [imageLoaded, setImageLoaded] = useState({});
    const handleLoadImage = (image) => {
        setImageLoaded(prev => {...prev, [image]: true});
    }
    const isLoading = useMemo(
        () => !(imagedLoaded.img1 && imagedLoaded.img2 && imagedLoaded.img3 && imagedLoaded.img4 && imagedLoaded.img5 && imagedLoaded.img6 && imagedLoaded.img7)
        , [imageLoaded] 
    )
    return (
        <Loading isLoading={isLoading} >
            <img onLoad={() => handleLoadImage("img1")} src={imgURL1} alt='image' />
            <img onLoad={() => handleLoadImage("img2")} src={imgURL2} alt='image' />
            <img onLoad={() => handleLoadImage("img3")} src={imgURL3} alt='image' />
            <img onLoad={() => handleLoadImage("img4")} src={imgURL4} alt='image' />
            <img onLoad={() => handleLoadImage("img5")} src={imgURL5} alt='image' />
            <img onLoad={() => handleLoadImage("img6")} src={imgURL6} alt='image' />
            <img onLoad={() => handleLoadImage("img7")} src={imgURL7} alt='image' />
        </Loading>
    )
}

Upvotes: 1

Related Questions