JAVASCRIPT

Detect Element Visibility with `useIntersectionObserver` Hook

Implement a custom `useIntersectionObserver` hook in React to efficiently detect when an element enters or exits the viewport, perfect for lazy loading images, implementing infinite scrolling, or triggering animations as elements become visible.

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

const useIntersectionObserver = (options) => {
  const [entry, setEntry] = useState(null);
  const [node, setNode] = useState(null);

  const observer = useRef(null);

  useEffect(() => {
    if (observer.current) {
      observer.current.disconnect();
    }

    if (node) {
      observer.current = new IntersectionObserver(([currentEntry]) => setEntry(currentEntry), options);
      observer.current.observe(node);
    }

    return () => {
      if (observer.current) {
        observer.current.disconnect();
      }
    };
  }, [node, options]); // Re-run if node or options change

  return [setNode, entry];
};

export default useIntersectionObserver;

/* Example Usage (in a component):
import React, { useRef, useEffect } from 'react';
import useIntersectionObserver from './useIntersectionObserver'; // Assuming useIntersectionObserver.js

function LazyImage({ src, alt }) {
  const [setRef, entry] = useIntersectionObserver({ threshold: 0.1 }); // Trigger when 10% of element is visible
  const isVisible = entry?.isIntersecting;

  return (
    <div
      ref={setRef} // Assign the ref to the element you want to observe
      style={{
        minHeight: '200px', // Give it some height to be observable
        display: 'flex',
        alignItems: 'center',
        justifyContent: 'center',
        border: '1px solid #ccc',
        marginBottom: '20px',
        backgroundColor: isVisible ? 'lightgreen' : '#f0f0f0'
      }}
    >
      {isVisible ? (
        <img src={src} alt={alt} style={{ maxWidth: '100%', maxHeight: '180px' }} />
      ) : (
        <p>Scroll down to load image...</p>
      )}
    </div>
  );
}

function App() {
  return (
    <div style={{ height: '200vh', padding: '20px' }}>
      <h1>Scroll Down to See Lazy Loaded Images</h1>
      <div style={{ height: '80vh', border: '1px dashed blue', marginBottom: '20px', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
        <p>Some content before images</p>
      </div>
      <LazyImage src="https://via.placeholder.com/400x200?text=Image+1" alt="Placeholder Image 1" />
      <LazyImage src="https://via.placeholder.com/300x150?text=Image+2" alt="Placeholder Image 2" />
      <LazyImage src="https://via.placeholder.com/500x250?text=Image+3" alt="Placeholder Image 3" />
      <div style={{ height: '80vh', border: '1px dashed blue', marginTop: '20px', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
        <p>Some content after images</p>
      </div>
    </div>
  );
}

export default App;
*/
How it works: This custom `useIntersectionObserver` hook provides a reactive way to detect when a specified DOM element enters or exits the browser's viewport. It leverages the native `IntersectionObserver` API, which offers a more performant alternative to scroll event listeners for checking element visibility. The hook returns a ref setter function (to be assigned to the target element) and an `IntersectionObserverEntry` object, from which you can derive `isIntersecting` status and other properties. This is highly useful for implementing features like lazy loading images, infinite scrolling, or triggering animations when elements become visible, significantly enhancing application performance and user experience.

Need help integrating this into your project?

Our team of expert developers can help you build your custom application from scratch.

Hire DigitalCodeLabs