Skip to content

fix(runtime): keep isQueuedForUpdate guard active under taskQueue: 'immediate'#6788

Open
erlanzhou wants to merge 1 commit into
stenciljs:mainfrom
erlanzhou:fix/immediate-reentrancy-guard
Open

fix(runtime): keep isQueuedForUpdate guard active under taskQueue: 'immediate'#6788
erlanzhou wants to merge 1 commit into
stenciljs:mainfrom
erlanzhou:fix/immediate-reentrancy-guard

Conversation

@erlanzhou

Copy link
Copy Markdown

What is the current behavior?

GitHub Issue Number: #6787

Under taskQueue: 'immediate' the HOST_FLAGS.isQueuedForUpdate reentrancy/dedup guard is inert: both its set (scheduleUpdate) and clear (callRender) were gated behind BUILD.taskQueue, which is false in immediate mode. Since immediate dispatches renders synchronously, a state write during render() (e.g. measure DOM then set state for the final DOM for text middle truncation) synchronously re-enters the render — unbounded, and able to corrupt an in-flight vdom patch (Cannot read properties of null (reading 'nodeType')).

#6673 patched one entry point (reflected bool attr removal); this further addresses the root cause.

What is the new behavior?

The set/clear of isQueuedForUpdate is now gated on BUILD.updatable only. The flag correctly reflects "an update is scheduled/in-progress" in every scheduling mode, so setValue/forceUpdate coalesce a state write made during render instead of re-entering.

  • async / congestionAsync: no behavior change (BUILD.taskQueue is true, so both conditions evaluate the same as before).
  • immediate: renders stay synchronous for external updates, but an in-render write no longer causes a synchronous reentrant re-render.

Documentation

Does this introduce a breaking change?

  • Yes
  • No

Immediate mode still dispatches renders synchronously; only removed the unbounded re-entry.

Testing

  • New regression test src/runtime/test/immediate-reentrancy.spec.tsx asserts that one external update produces exactly one follow-up render in both async and immediate (drives the real runtime with BUILD.taskQueue toggled). Fails before the fix (unbounded), passes after.
  • Full npx jest src/runtime — 651 tests / 64 suites green.

Other information

…mmediate'

Both the set (scheduleUpdate) and clear (callRender) of HOST_FLAGS.isQueuedForUpdate
were gated on BUILD.taskQueue, so under taskQueue: 'immediate' the render reentrancy
guard was inert. Because immediate dispatches synchronously, a state write during
render() (e.g. measure-then-setState) re-entered the render unbounded and could
corrupt an in-flight vdom patch. Gate the flag on BUILD.updatable only;
async/congestionAsync behavior is unchanged. Complements stenciljs#6673.
@erlanzhou
erlanzhou requested a review from a team as a code owner July 20, 2026 00:27
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