logoESLint React

no-leaked-intersection-observer

Enforces that every 'IntersectionObserver' created in a component or custom hook has a corresponding 'IntersectionObserver.disconnect()'.

Full Name in eslint-plugin-react-web-api

react-web-api/no-leaked-intersection-observer

Full Name in @eslint-react/eslint-plugin

@eslint-react/web-api-no-leaked-intersection-observer

Presets

web-api recommended recommended-typescript recommended-type-checked strict strict-typescript strict-type-checked

Rule Details

Creating a IntersectionObserver without disconnecting it can lead to memory leaks and unexpected behavior.

Examples

Creating IntersectionObserver without disconnecting in useEffect

import React, { useEffect, useRef } from "react";

function MyComponent() {
  const ref = useRef<HTMLDivElement>(null);

  useEffect(() => {
    if (!ref.current) return;
    // Problem: A 'IntersectionObserver' in 'useEffect' should have a corresponding 'intersectionObserver.disconnect()' in its cleanup function.
    const ro = new IntersectionObserver(() => console.log("intersection"));
    ro.observe(ref.current);
  }, []);

  return <div ref={ref} />;
}

Disconnecting IntersectionObserver in useEffect cleanup

import React, { useEffect, useRef } from "react";

function MyComponent() {
  const ref = useRef<HTMLDivElement>(null);

  useEffect(() => {
    if (!ref.current) return;
    // Recommended: Create and use IntersectionObserver
    const ro = new IntersectionObserver(() => console.log("intersection"));
    ro.observe(ref.current);
    // Recommended: Disconnect in the cleanup function
    return () => ro.disconnect();
  }, []);

  return <div ref={ref} />;
}

Observing multiple elements with IntersectionObserver

When observing multiple elements, disconnect the observer in the cleanup function to prevent memory leaks.

// Recommended: Disconnect IntersectionObserver when observing multiple elements
import { useEffect, useRef } from "react";

function ResizablePanels() {
  const panelARef = useRef<HTMLDivElement>(null);
  const panelBRef = useRef<HTMLDivElement>(null);

  useEffect(() => {
    const ro = new IntersectionObserver((entries) => {
      for (const entry of entries) {
        console.log("Size changed:", entry.contentRect);
      }
    });

    if (panelARef.current) ro.observe(panelARef.current);
    if (panelBRef.current) ro.observe(panelBRef.current);

    return () => ro.disconnect();
  }, []);

  return (
    <>
      <div ref={panelARef}>Panel A</div>
      <div ref={panelBRef}>Panel B</div>
    </>
  );
}

Resources

Further Reading


See Also

On this page