skip to Main Content

I have an image that is reloaded every 1 min. Every time 1 min is completed, the new image is fetched. (I use the embedded link inside the src attribute of img), like <img src={link} />.

The problem is while a new image is loading and fetching, that image component is disappeared and later when the new image is fetched, it will show that new image.
My requirement is to show loading… over the previous image before the new image is loaded and replaces the old one.
In short, I don’t want the old one to disappear and a new one appears. I want to keep an old one with loading overlay text and replace that image with a new content and hide loading… text.

  const [isLoading, setIsLoading] = useState(true);

inside return:

 <div className={styles.wrapper} style={{ opacity: isLoading ? "0.3" : "1" }}>
    <img src={src} onLoad={() => setIsLoading(false)} />
    <div>
      {isLoading && (
        <div className={styles.overlayLoading}>
          Loading...
        </div>
      )}
    </div>
  </div>

3

Answers


  1. You need to create an extra hidden img tag that you use to load the new image, then when the onload event fires, you can switch the src attribute in the existing img tag to the new image, which will update straight away, as the image is now locally cached.

    A few examples on how to do this can be found on this question Show "loading" while a new image is loading

    Login or Signup to reply.
  2. You can preload image with useEffect, your browser will cache response from server, and when you will request same image with same url, browser will take that image from cache. And also you give your browser some milliseconds to render a new image and then you can toggle loading state.

      const [isLoading, setIsLoading] = useState(true);
      const [cachedImageUrl, setCachedImageUrl] = useState("");
    
      useEffect(() => {
       setIsLoading(true);
       const image = new Image();
    
       image.onload = () => {
         setCachedImageUrl(src);
         const renderedInterval = setTimeout(() => setIsLoading(false), 100);
       }
    
       image.src = src;
    
       return () => {
         image.onload = ()=>{}
         clearInterval(renderedInterval); 
       }
      }, [src]);
    
      return (
        <div className={styles.wrapper} style={{ opacity: isLoading ? "0.3" : "1" }}>
        <img src={cachedImageUrl} />
        <div>
          {isLoading && (
            <div className={styles.overlayLoading}>
              Loading...
            </div>
          )}
        </div>
      </div>
      );
    
    Login or Signup to reply.
  3. I don’t know how familiar you are with RTK query but it seems like it’s the best tool for the job. RTK query works best with interval fetching.

    Check out this example and this return value.

    Note: below code has not been tested.

    Now in your case, you can do something like:

    const { currentData, error, refetch, isFetching } = useGetImageQuery(name, {
    pollingInterval: 60000,
    })
    
    <div className={styles.wrapper} style={{ opacity: isFetching ? "0.3" : "1" }}>
      <img src={currentData.src} />
      <div>
        {isFetching && (
          <div className={styles.overlayLoading}>
            Loading...
          </div>
        )}
      </div>
    </div>
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search