Skip to content

fix: batch globalResizeObserver callbacks via requestAnimationFrame#453

Open
chrisnojima wants to merge 2 commits into
LegendApp:mainfrom
chrisnojima:fix/resize-observer-loop-notifications
Open

fix: batch globalResizeObserver callbacks via requestAnimationFrame#453
chrisnojima wants to merge 2 commits into
LegendApp:mainfrom
chrisnojima:fix/resize-observer-loop-notifications

Conversation

@chrisnojima

Copy link
Copy Markdown

Hi,

I've been using 3.0.0-beta.56 and I ran into an issue w/ my list on web (electron). If i set my drawDistance higher when going into my chat app I'd get a 'ResizeObserver loop completed with undelivered notifications' error immediately. This fixes it for me.

AI summary below:

Calling ResizeObserver callbacks synchronously causes "ResizeObserver loop completed with undelivered notifications" errors in browsers/Electron when a callback triggers layout changes that generate new resize observations before the current notification batch is fully delivered.

Defer callback dispatch through requestAnimationFrame, coalescing all entries that arrive within the same frame into a single flush. This matches the standard pattern for ResizeObserver implementations and eliminates the loop error without changing observable behavior for callers.```

Calling ResizeObserver callbacks synchronously causes "ResizeObserver loop
completed with undelivered notifications" errors in browsers/Electron when
a callback triggers layout changes that generate new resize observations
before the current notification batch is fully delivered.

Defer callback dispatch through requestAnimationFrame, coalescing all
entries that arrive within the same frame into a single flush. This matches
the standard pattern for ResizeObserver implementations and eliminates the
loop error without changing observable behavior for callers.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: df0f9f6f71

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/hooks/createResizeObserver.ts Outdated
pending = pending.concat(entries);
if (!ticking) {
ticking = true;
requestAnimationFrame(() => {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Guard ResizeObserver batching when requestAnimationFrame is absent

This callback path now assumes requestAnimationFrame exists whenever ResizeObserver exists, but createResizeObserver only feature-detects ResizeObserver. In environments that provide/polyfill ResizeObserver without a global rAF (e.g., some headless or test runtimes), the first resize notification will throw and permanently break layout syncing for observed elements. Add an rAF availability check with a fallback scheduler (like setTimeout) before enabling batching.

Useful? React with 👍 / 👎.

Comment on lines +20 to +27
globalThis.ResizeObserver = class MockResizeObserver {
constructor(cb: ResizeObserverCallback) {
capturedResizeCallback = cb;
}
observe() {}
unobserve() {}
disconnect() {}
} as unknown as typeof ResizeObserver;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Restore ResizeObserver global after test file setup

The test file replaces globalThis.ResizeObserver at module scope but never restores it, so later test files can inherit this mock and run against a no-op observer instead of the real/fallback implementation. Because Bun test runs files in a shared process, this creates order-dependent cross-file contamination and can hide or introduce failures outside this suite. Restore globalThis.ResizeObserver in teardown to keep tests isolated.

Useful? React with 👍 / 👎.

@chrisnojima

Copy link
Copy Markdown
Author

Actually, maybe this is a side effect of me doing my own paging. When I switched to onEndReached its fine.

@chrisnojima chrisnojima reopened this May 27, 2026
@chrisnojima

Copy link
Copy Markdown
Author

Actually I think this is still a real issue. If i'm quickly switching my chat threads I can get this to happen still.

…ications

requestAnimationFrame still runs within the same ResizeObserver processing
cycle, which can trigger 'ResizeObserver loop completed with undelivered
notifications'. Using setTimeout(fn, 0) defers processing to the macrotask
queue, ensuring the entire synchronous burst of resize events accumulates
before any callback runs.
@chrisnojima

Copy link
Copy Markdown
Author

And after some back and forth I think a RAF isn't enough, you have to debounce it. So strangely I see this in my webpack overlay, but it won't show in console or if you breakpoint on caught/uncaught exceptions. Some prior discussions: https://stackoverflow.com/questions/76187282/how-to-fix-resizeobserver-loop-completed-with-undelivered-notifications

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.

1 participant