Skip to content

fix: resolve infinite render loop for dropdowns inside Dialog - #122

Merged
mathewtaylor merged 2 commits into
mainfrom
fix/portal-render-loop-wasm
Feb 10, 2026
Merged

fix: resolve infinite render loop for dropdowns inside Dialog#122
mathewtaylor merged 2 commits into
mainfrom
fix/portal-render-loop-wasm

Conversation

@mathewtaylor

Copy link
Copy Markdown
Contributor

Summary

  • Fixed infinite render loop in PortalHost that froze WASM apps when opening dropdown controls (Select, Combobox, MultiSelect) inside a Dialog
  • Replaced _pendingRerender flag with structural change detection — compares rendered portal keys against the current portal set after each render cycle to distinguish between redundant RefreshPortal calls (ignored) and actual register/unregister changes (triggers re-render)
  • The bug also existed in Blazor Server but was masked by the circuit dispatcher's natural throttling

@mathewtaylor
mathewtaylor merged commit b4a1429 into main Feb 10, 2026
@mathewtaylor
mathewtaylor deleted the fix/portal-render-loop-wasm branch February 10, 2026 06:16
whis pushed a commit to whis/ui that referenced this pull request Jul 19, 2026
…K window (blazorblueprintui#418)

On Blazor Server, BbCategoryPortalHost dropped portal content-only
notifications (RefreshPortal, same portal id) that arrived while
_isRendering was latched across the render→ACK round-trip, and the
post-render catch-up only re-rendered on portal key-set changes — so a
Dialog/Sheet whose owner updated state faster than one SignalR ACK froze
on stale content. Regression of the blazorblueprintui#118/blazorblueprintui#119 class reintroduced by blazorblueprintui#122.

Record a deferred re-render for content-only drops and flush it through a
coalesced, yield-deferred pass:
- _pendingRerender recovers the dropped update
- _isFlushing suppresses refreshes raised by the flush's own render so a
  nested same-category portal (e.g. Select-in-Popover) cannot loop
- _flushScheduled + Task.Yield run the flush as a fresh dispatcher work
  item, never a synchronous nested re-render (which stack-overflows on
  WebAssembly), with a disposed guard on the fire-and-forget continuation

Structural (register/unregister) recovery stays synchronous, unchanged.
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