DEV Community

Cover image for How to detect images loaded in React
Alejandro Martinez
Alejandro Martinez

Posted on • Updated on

How to detect images loaded in React

When I performed a manual deep linking hook in a web application, the automatically scrolling down to a specific section caused a delay by loading of images.

How to detect the loading issues of the images before executing any action in react? The next hook uses eventListener with load and errorevents, and detects the HTMLImageElement.complete property of javascript, to determine if all images in a specific wrapper element have been completed.

import { useState, useEffect, RefObject } from "react";

export const useOnLoadImages = (ref: RefObject<HTMLElement>) => {
  const [status, setStatus] = useState(false);

  useEffect(() => {
    const updateStatus = (images: HTMLImageElement[]) => {
      setStatus(
        images.map((image) => image.complete).every((item) => item === true)
      );
    };

    if (!ref?.current) return;

    const imagesLoaded = Array.from(ref.current.querySelectorAll("img"));

    if (imagesLoaded.length === 0) {
      setStatus(true);
      return;
    }

    imagesLoaded.forEach((image) => {
      image.addEventListener("load", () => updateStatus(imagesLoaded), {
        once: true
      });
      image.addEventListener("error", () => updateStatus(imagesLoaded), {
        once: true
      });
    });

    return;
  }, [ref]);

  return status;
};
Enter fullscreen mode Exit fullscreen mode

Note: is important to add both load and error to avoid any blocking after load page.

According with the documentation of complete prop, the image is considered completely loaded if any of the following are true:

  • Neither the src nor the srcset attribute is specified. The srcset attribute is absent and the src attribute, while specified, is the empty string ("").
  • The image resource has been fully fetched and has been queued for rendering/compositing.
  • The image element has previously determined that the image is fully available and ready for use.
  • The image is "broken;" that is, the image failed to load due to an error or because image loading is disabled.

To use it you have to pass a ref wrapper to limit the search images.

import { useRef } from "react";
import { useOnLoadImages } from "./hooks/useOnLoadImages";
import "./styles.css";

export default function App() {
  const wrapperRef = useRef<HTMLDivElement>(null);
  const imagesLoaded = useOnLoadImages(wrapperRef);

  return (
    <div className="App" ref={wrapperRef}>
      <h2>How to detect images loaded in React</h2>
      <div>
        <p>{!imagesLoaded ? "Loading images..." : "Images loaded"}</p>
        <img src="https://source.unsplash.com/1600x900/?nature" alt="nature" />
        <img src="https://source.unsplash.com/1600x900/?water" alt="water" />
        <img src="https://source.unsplash.com/1600x900/?animal" alt="animal" />
        <img src="https://source.unsplash.com/1600x900/?lake" alt="lake" />
        <img src="https://source.unsplash.com/1600x900/?life" alt="life" />
      </div>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

Here there are a demo Link (reload internal browser)

If you like the article follow me in:

Discussion (2)

Collapse
aleksandrhovhannisyan profile image
Aleksandr Hovhannisyan

Note that this will run into a race condition if you set the src attribute on your images before you register their load listeners. I was able to reproduce this by refreshing the Codesandbox demo iframe; the images load very quickly because they were cached by the browser. Unfortunately, this means that by the time the event listener is registered, the load event will have already fired. So the message is never displayed.

See here for more context: stackoverflow.com/questions/146485....

Collapse
alejomartinez8 profile image
Alejandro Martinez Author • Edited on

Yes you're right, I had to use it in an app when the content comes from a CMS and we parse it, and normally the images aren't cached. I'm going to review it on this case. Thanks.