DEV Community

Cover image for How to retry image when error occurs in React?
Anoop Mundathan
Anoop Mundathan

Posted on

2 1

How to retry image when error occurs in React?

Problem

I am trying to load an image and in my case image is hosted in AWS S3 bucket, for some strange reason, image is not available immediately for client to consume and was experiencing a broken thumbnail image in frontend.

Image is getting ready after couple of seconds. So in this case, I wanted to retry image url again until it’s available.

I am using onerror event handler on this purpose, which gets called when error occurs while loading or rendering an image.

Step 1:

First I wanted to keep track number of retries. For this I created a useRef variable.

const componentRef = useRef<number>();
Enter fullscreen mode Exit fullscreen mode

Step 2:

Initialise ref variable in first render. This is done in useEffect hook.

useEffect(() => {
    componentRef.current = RETRY_COUNT;
}, []);
Enter fullscreen mode Exit fullscreen mode

Step 3:

Add onError handler in img tag.

<Image src={s3Url} alt={filename} onError={handleError} />
Enter fullscreen mode Exit fullscreen mode

Step 4:

Set error state, Set image source and decrease retry count.

 const handleError = useCallback(({ currentTarget }) => {

    setError(true);
    if (componentRef && componentRef.current && componentRef.current > 0) {
      setTimeout(() => {
        currentTarget.onerror = null;
        currentTarget.src = s3Url;
        componentRef.current =
          componentRef && componentRef.current && componentRef.current - 1;
      }, RETRY_DELAY);
    }
  }, []);
Enter fullscreen mode Exit fullscreen mode

I have wrapped this logic in setTimeout with 1 second delay, so that this retry will happen in each second.

As a final step to improve UX, you may show loading indicator while image is being fetched. For that you could create a state variable as below.

const [error, setError] = useState<boolean>(false);
Enter fullscreen mode Exit fullscreen mode

Set error state

const handleError = useCallback(({ currentTarget }) => {
    setError(true);
});
Enter fullscreen mode Exit fullscreen mode

Add onload handler in image tag.

<Image onLoad={handleLoad} />
Enter fullscreen mode Exit fullscreen mode

Set error state false.

const handleLoad = useCallback(() => {
    setError(false);
  }, []);

Enter fullscreen mode Exit fullscreen mode

Show loading indicator

if (error) {
     return <Loading />
}
Enter fullscreen mode Exit fullscreen mode

And finally, here is the full source code.

import React, {
  useCallback,
  useEffect,
  useMemo,
  useRef,
  useState
} from "react";
import styled from "styled-components";
import { Loading } from "./loading";

const RETRY_COUNT = 5;
const RETRY_DELAY = 1000;

const Image = styled.img`
  width: 100%;
  height: 100%;
  border-radius: 6px;
`;

interface ImagePreviewProps {
  filename: string;
  s3Url: string;
}

export const ImagePreview: React.FC<ImagePreviewProps> = ({
  filename,
  s3Url,
  loading
}) => {

  const componentRef = useRef<number>();
  const [error, setError] = useState<boolean>(false);

  useEffect(() => {
    componentRef.current = RETRY_COUNT;
  }, []);

  const handleError = useCallback(({ currentTarget }) => {
    setError(true);
    if (componentRef && componentRef.current && componentRef.current > 0) {
      setTimeout(() => {
        currentTarget.onerror = null;
        currentTarget.src = s3Url;
        componentRef.current =
          componentRef && componentRef.current && componentRef.current - 1;
      }, RETRY_DELAY);
    }
  }, []);

  const handleLoad = useCallback(() => {
    setError(false);
  }, []);

  const showImage = useMemo(() => {
    return (
          <Image
            src={s3Url}
            alt={filename}
            onError={handleError}
            onLoad={handleLoad}
          />
        )
    );
  }, [loading, filename, s3Url]);

  if (error) {
     return <Loading />
  }

  return showImage;
};
Enter fullscreen mode Exit fullscreen mode

SurveyJS custom survey software

Simplify data collection in your JS app with a fully integrated form management platform. Includes support for custom question types, skip logic, integrated CCS editor, PDF export, real-time analytics & more. Integrates with any backend system, giving you full control over your data and no user limits.

Learn more

Top comments (3)

Collapse
 
riyas07 profile image
Mohammed Riyas

Bro pls explain your project. Iam unable to understand what it does

Collapse
 
anoopmundathan profile image

Updated. Please let me know if this helpful.

Collapse
 
riyas07 profile image
Mohammed Riyas

Yupp it will thank you bro

A Workflow Copilot. Tailored to You.

Pieces.app image

Our desktop app, with its intelligent copilot, streamlines coding by generating snippets, extracting code from screenshots, and accelerating problem-solving.

Read the docs

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay