Conversation
…e corruption (bvaughn#731) On iPadOS Safari, backgrounding the app (App Switcher) fires a rapid sequence of ResizeObserver events with transiently wrong container widths (e.g. 1180px -> 820px -> 585px -> 1180px within ~700ms). During the transient narrow reading, a Panel with a pixel minSize triggers a percentage inflation that is never re-derived when the container returns to its real width, because the preserve-relative-size behavior keeps the inflated percentage. The fix adds a short (50ms) debounce to the Group element's ResizeObserver callback. Only the final settled size in a burst is applied, so the transient narrow readings never corrupt the layout. Fixes bvaughn#731
|
@waterWang is attempting to deploy a commit to the Brian Vaughn's projects Team on Vercel. A member of the Team first needs to authorize it. |
| if (resizeTimer !== null) { | ||
| clearTimeout(resizeTimer); | ||
| } | ||
| resizeTimer = setTimeout(handleGroupResize, 50); |
There was a problem hiding this comment.
How did you determine 50ms to be a good debounce interval?
There was a problem hiding this comment.
I am uneasy about adding debounce in the way that's proposed here. I think it's too broad a change and I can see it breaking use cases other than the one reported in #731.
I would prefer to listen for "visibilitychange" events and just ignore ResizeObserver when document.visibilityState === "hidden" (see#735). Unfortunately this seems trickier than I assumed, because iOS Safari runs the ResizeObserver callbacks right before the "visibilitychange" event fires. Same for other events we could potentially listen to, like "blur".
I've also checked projects like Chrome's page-lifecycle package and confirmed the sequencing is similarly "broken" for those.
I'm not sure yet of the best way to proceed. I think if we need to add some sort of debounce to cover this case, I'd prefer to scope it to iOS devices only.
Summary
Fixes #731 — iPadOS Safari layout corruption after backgrounding the app.
Root Cause
On iPadOS Safari, backgrounding the app (via the App Switcher) fires a rapid sequence of
ResizeObserverevents with transiently wrong container widths. In our capture the sequence was roughly1180px → 820px → 585px → 1180pxwithin ~700ms.During the transient narrow reading (e.g. 585px), a
Panelwith a pixelminSize(e.g. 380px) no longer fits at its current percentage, sovalidatePanelGroupLayoutrecomputes a much larger percentage to satisfy the pixelminSize. Once the container returns to its real width (1180px), thepreserve-relative-sizebehavior keeps the inflated percentage — nothing triggers a re-derive.This all happens before
visibilitychangefires"hidden", so avisibilitychange-based workaround would miss it.Fix
Debounce the Group element's
ResizeObservercallback with a short (50ms) timeout. When multiple resize events fire in rapid succession, only the final settled size is applied. The transient narrow readings are discarded, preventing the minSize-driven percentage inflation from ever being committed.onResizenotifications are not debounced (they fire immediately for eachResizeObserverentry).unmountGroupfunction.Testing
ResizeObserverfires synchronously and does not simulate iPadOS's transient burst pattern.ResizeObserver).Related