iTranslated by AI

The content below is an AI-generated translation. This is an experimental feature, and may contain errors. View original article
🖥️

[React] Custom Hook to Detect if an Element is Visible, Above, or Below the Viewport

に公開

In React, I created a custom hook (though I ended up not using it) to check whether a specific element currently falls into one of the following categories:

  • Inside the screen (within the viewport)
  • Above the screen
  • Below the screen

I'm sharing it here as a small tribute. It uses the Intersection Observer API for better performance.

Creating the Custom Hook

useOnScreen.tsx
import React, { useState, useEffect } from 'react';

type TargetViewPosition = undefined | 'ABOVE_VIEWPORT' | 'BELOW_VIEWPORT' | 'VISIBLE';

export function useOnScreen(targetRef: React.RefObject<HTMLElement>) {
  const [targetViewPosition, setTargetViewPosition] = useState<
    TargetViewPosition
  >(undefined);

  const observer = new IntersectionObserver(
    ([entry]) => {
      if (entry.isIntersecting) {
        setTargetViewPosition('VISIBLE'); // Visible on screen
        return;
      }
      if (entry.boundingClientRect.top > 0) {
        setTargetViewPosition('BELOW_VIEWPORT'); // Positioned below the screen
      } else {
        setTargetViewPosition('ABOVE_VIEWPORT'); // Positioned above the screen
      }
    },
    {
      root: null,
      threshold: 0,
    }
  );

  useEffect(() => {
    // Register the observer on mount
    if (targetRef.current) observer.observe(targetRef.current);
 
    // Disconnect the observer on unmount
    return () => {
      observer.disconnect();
    };
  }, []);

  return targetViewPosition;
}

Usage

↓ Pass the ref of the element you want to track to the custom hook's argument.

SomeComponent.tsx
import { useOnScreen } from "./useOnScreen.tsx"

export const SomeComponent: React.VFC = () => {
  const targetViewPosition = useOnScreen();
  return (
    <>
      {targetViewPosition === 'VISIBLE' && <p>Visible on screen</p>}
      {targetViewPosition === 'ABOVE_VIEWPORT' && <p>Positioned above the screen</p>}
      {targetViewPosition === 'BELOW_VIEWPORT' && <p>Positioned below the screen</p>}
      <div ref={targetRef}>Target element for position checking</div>
    </>
  )
}

There might not be many use cases for it, though.

Discussion