Skip to content

feat: no-leaked-intersection-observer, closes #1841#1868

Open
Netail wants to merge 5 commits into
Rel1cx:mainfrom
Netail:feat/no-leaked-intersection-observer
Open

feat: no-leaked-intersection-observer, closes #1841#1868
Netail wants to merge 5 commits into
Rel1cx:mainfrom
Netail:feat/no-leaked-intersection-observer

Conversation

@Netail

@Netail Netail commented Jun 11, 2026

Copy link
Copy Markdown

What kind of change does this PR introduce?

Check at least one. If you are introducing a new binding, you must reference an issue where this binding has been proposed, discussed and approved by the maintainers.

  • Bugfix
  • Feature
  • Perf
  • Docs
  • Test
  • Chore
  • Enhancement
  • New Binding issue #___
  • Code style update
  • Refactor
  • Build-related changes
  • Other, please describe:

Does this PR introduce a breaking change?

If yes, please describe the impact and migration path for existing applications in an attached issue.

  • Yes, and the changes were approved in issue #___
  • No

Checklist

  • When resolving issues, they are referenced in the PR's title (e.g fix: remove a typo, closes #___, #___)
  • I have added a convincing reason for adding this feature, if necessary

Other information

@vercel

vercel Bot commented Jun 11, 2026

Copy link
Copy Markdown

@Netail is attempting to deploy a commit to the Rel1cx's projects Team on Vercel.

A member of the Team first needs to authorize it.

@vercel

vercel Bot commented Jun 11, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
eslint-react Ready Ready Preview, Comment Jun 11, 2026 9:07pm

@Netail

Netail commented Jun 11, 2026

Copy link
Copy Markdown
Author

Technically, you could make a single no-leaked-observer rule, as they are pretty much the same. Then you could also cover MutationObserver

Rel1cx
Rel1cx previously requested changes Jun 11, 2026

@Rel1cx Rel1cx left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The "observe-once" pattern is high-frequency usage specific to IO, no-leaked-intersection-observer.mdx should add a section dedicated to examples, demonstrating the recommended approach (adding disconnect as a fallback in the callback):

useEffect(() => {
  const observer = new IntersectionObserver(([entry], obs) => {
    if (entry.isIntersecting) {
      setVisible(true);
      obs.disconnect(); // observe-once
    }
  });
  observer.observe(ref.current);
  return () => observer.disconnect(); // still need a fallback: might be unmounted before the callback runs
}, []);

@Rel1cx

Rel1cx commented Jun 11, 2026

Copy link
Copy Markdown
Owner

Technically, you could make a single no-leaked-observer rule, as they are pretty much the same. Then you could also cover MutationObserver

Thanks for the suggestion, but we intentionally keep these as separate rules.

While the implementations look nearly identical today, each observer type has its own set of legitimate usage patterns that the rule needs to special-case over time:

  • IntersectionObserver: the observe-once pattern is idiomatic, such as lazy loading with obs.disconnect() or unobserve(entry.target) inside the callback
  • MutationObserver: has takeRecords(), and disconnecting often involves flushing pending records
  • PerformanceObserver: different observe({ type, buffered }) signature, no element argument at all

Merging them into a single no-leaked-observer rule would mean handling all of these divergences inside one implementation, documenting every observer's exceptions on one page, and multiplying the test matrix accordingly. Separate rules keep the semantics, docs, and tests self-contained, and let users enable/disable each observer type independently.

The code duplication is deliberate for the same reason: these rules are expected to diverge as observer-specific handling lands, so copy-then-diverge gives us that freedom without premature abstraction.

For this PR specifically, that's also why I'd like to see the IO-specific patterns covered explicitly: a test case for the observe-once callback-parameter pattern, and a docs example showing the recommended way to write it:

useEffect(() => {
  const observer = new IntersectionObserver(([entry], obs) => {
    if (entry.isIntersecting) {
      setVisible(true);
      obs.disconnect(); // observe-once
    }
  });
  observer.observe(ref.current);
  return () => observer.disconnect(); // still need a fallback: might be unmounted before the callback runs
}, []);

This example would be meaningless in any other observer's docs, but without it here, users writing observe-once code will likely report the rule's warning as a false positive. That's exactly the kind of per-observer content that justifies keeping the rules separate.

Signed-off-by: REL1CX <solarflarex@qq.com>
Signed-off-by: REL1CX <solarflarex@qq.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants