fix: components mounted via mount() during onMount() that not properly update when using signals provided as input of the mount() function. #16066
+107
−12
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Fixes #15870.
Fix: First
{#if}
block fails to update in programmatically mounted componentsIssue Description
Bug: When components are mounted programmatically using the
mount()
API with context-passed state, the first conditional block ({#if}
) in the mounted component fails to update when the state changes. Subsequent conditional blocks work correctly.According to my analysis this commit introduced the issue: d2e7932
Reproduction:
Root Cause Analysis
The issue stems from a dependency registration failure in the reactivity system. Here's what happens:
mount()
API, components are rendered within a mount effect context{#if}
block accessesstateFromContext.value
, triggering proxy property creationproxy.js
where created usingstate()
instead ofsource()
state()
callspush_reaction_value()
, adding the signal to thereaction_sources
arrayget()
function checks!reaction_sources?.includes(signal)
, it returnsfalse
{#if}
block effect never gets registered in the signal'sreactions
arrayKey insight: This only affects the first conditional because subsequent ones hit different code paths in the reactivity system.
The Fix
Location:
svelte/src/internal/client/proxy.js
The issue was traced to how proxy objects create signals for their properties. Originally, the code was importing
state
but aliasing it assource
, which created confusion:Before:
After:
I made a minimal change to fix the reported issue by switching to use
source()
only in the proxy'sget
trap where the problematic dependency registration was occurring, while keepingstate()
everywhere else in the proxy code. This represents the minimal change needed to resolve the issue, though it may not be the optimal long-term solution since I lack deep understanding of Svelte's reactivity system internals.The key difference is that
source()
creates a signal without side effects, whilestate()
creates a signal and callspush_reaction_value()
which adds it to thereaction_sources
array. This fix ensures that when proxy properties are first accessed, they don't get inappropriately added to thereaction_sources
array, allowing normal dependency registration to proceed and making programmatic mounting behave consistently with template rendering.Before submitting the PR, please make sure you do the following
feat:
,fix:
,chore:
, ordocs:
.packages/svelte/src
, add a changeset (npx changeset
).Tests and linting
pnpm test
and lint the project withpnpm lint