Get Even More Visitors To Your Blog, Upgrade To A Business Listing >>

Lazy-Loading Image

  1. Use the useState hook to create a stateful value called isLoaded that indicates if the image has been loaded.
  2. Use the useEffect hook to check if the HTMLImageElement.prototype contains the ‘loading’ property.
    • If it does, do nothing.
    • If it does not, create a new IntersectionObserver instance.
  3. Use the useCallback hook to memoize a callback function for the IntersectionObserver.
  4. Use the useRef hook to create a ref for the element and another ref for the IntersectionObserver instance (if necessary).
  5. Use IntersectionObserver.observe to observe the element and pass in the callback function created in step 3 as an argument.
  6. Render the element with the given attributes.
    • If lazy loading is not supported natively, set the loading attribute to ‘lazy’.
    • Use the isLoaded state variable to determine the value of the src attribute.
  7. In the callback function created in step 3:
    • Update the isLoaded state variable.
    • Use IntersectionObserver.disconnect to disconnect the IntersectionObserver instance.

Here is some sample code that demonstrates how to use the React hooks useState, useEffect, useCallback, and useRef to create a component that renders an image that supports lazy loading:

import { useState, useEffect, useCallback, useRef } from 'react';

function LazyImage(props) {
  const { src, alt } = props;
  const imageRef = useRef(null);
  const observerRef = useRef(null);
  const [isLoaded, setIsLoaded] = useState(false);

  const onIntersect = useCallback(
    ([entry]) => {
      if (entry.isIntersecting) {
        setIsLoaded(true);
        observerRef.current.disconnect();
      }
    },
    [setIsLoaded]
  );

  useEffect(() => {
    if ('loading' in HTMLImageElement.prototype) {
      setIsLoaded(true);
      return;
    }

    observerRef.current = new IntersectionObserver(onIntersect);
    observerRef.current.observe(imageRef.current);

    return () => {
      observerRef.current.disconnect();
    };
  }, [onIntersect]);

  return (
    
  );
}

This code defines a LazyImage component that takes an src and alt prop. It creates a ref for the element and another ref for the IntersectionObserver instance using the useRef hook. It also creates a stateful value called isLoaded using the useState hook. The onIntersect function is created using the useCallback hook and is used as a callback for the IntersectionObserver.

The useEffect hook is used to check if the HTMLImageElement.prototype contains the ‘loading’ property. If it does, the isLoaded state is set to true and the effect returns early. If it does not, a new IntersectionObserver instance is created and the onIntersect function is passed as a callback. The observerRef.current ref is then used to call observe on the IntersectionObserver instance, passing in the imageRef.current ref as an argument. When the component unmounts, the IntersectionObserver instance is disconnected using the observerRef.current ref.

Finally, the element is rendered with the given src and alt props. If lazy loading is not supported natively, the loading attribute is set to ‘lazy’. The isLoaded state is used to determine the value of the src attribute. If isLoaded is true, the src prop is used. If isLoaded is false, the src attribute is not rendered at all.

Unit Test using Jest

Here’s an example of a unit test using Jest and React Testing Library for the LazyImage component:

import { render, waitForElement } from '@testing-library/react';
import LazyImage from './LazyImage';

test('LazyImage component loads image when it is in the viewport', async () => {
  // mock the IntersectionObserver API
  window.IntersectionObserver = jest.fn(function() {
    this.observe = jest.fn();
    this.disconnect = jest.fn();
  });

  const src = 'image.jpg';
  const alt = 'test image';
  const { getByAltText } = render();

  // image should not be rendered initially
  expect(getByAltText(alt)).not.toBeVisible();

  // trigger the IntersectionObserver callback to load the image
  window.IntersectionObserver.mock.calls[0][0][0].isIntersecting = true;

  // wait for the image to be rendered
  const imageElement = await waitForElement(() => getByAltText(alt));

  // image should be rendered
  expect(imageElement).toBeVisible();
});

The post Lazy-Loading Image first appeared on ReactHub.



This post first appeared on Authentic React Development Bookmark Resources, please read the originial post: here

Share the post

Lazy-Loading Image

×

Subscribe to Authentic React Development Bookmark Resources

Get updates delivered right to your inbox!

Thank you for your subscription

×