Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

fix: top-most derived in a chain of deriveds marked as MAYBE_DIRTY when executed from a snippet $.fallback#16111

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Conversation

raythurnvoid
Copy link
Contributor

@raythurnvoidraythurnvoid commentedJun 9, 2025
edited
Loading

Fixes:#16090

The Problem

A chain of derived signals fails to update if an intermediate signal in the chain is also read inside a{#snippet} that has a parameter with a default value.

The root cause is a subtle interaction between how the snippet's fallback value is evaluated and how the reactivity system determines if a signal is "clean" or "dirty". This leads to an inconsistent state where the intermediate derived signal gets "stuck" and no longer propagates updates.

Detailed Breakdown

  1. Compiler Generates a Fallback Reaction

    The entire issue originates from the code the Svelte compiler generates for a snippet with a default parameter. A snippet like{#snippet dummy(value = 0)} is compiled into a structure that uses a$.fallback() call. This generated code then immediately reads the derived values within a special context.

    The compiled output looks like this:

    constdummy=$.wrap_snippet(_page,function($$anchor,$$arg0){$.validate_snippet_args(...arguments);// The fallback is wrapped in a derived and then immediately read by `$.get`letvalue=$.derived_safe_equal(()=>$.fallback($$arg0?.(),0));$.get(value);});

    When$.get(value) is executed, it runs in a context where askip_reaction flag is set totrue. This is the trigger for the entire bug.

  2. Initial Render: An Inconsistent State is Created

    When the$.get(value) from the compiled snippet runs, it reads bothderived1 andderived2. Becauseskip_reaction istrue, the following happens:

    First,derived1 is evaluated. Theupdate_derived function sees thatskip_reaction is true and incorrectly marksderived1 asMAYBE_DIRTY.

    Code frompackages/svelte/src/internal/client/reactivity/deriveds.js:

    exportfunctionupdate_derived(derived){// ...// Because `skip_reaction` is true, and the derived is `UNOWNED`,// the status is set to `MAYBE_DIRTY` instead of `CLEAN`.varstatus=(skip_reaction||(derived.f&UNOWNED)!==0)&&derived.deps!==null            ?MAYBE_DIRTY            :CLEAN;set_signal_status(derived,status);}

    Next,derived2 is evaluated. Thecheck_dirtiness function is called for it. Crucially, at the end of this function,derived2 is marked asCLEAN because it's being accessed within an active effect whereskip_reaction does not prevent the cleaning.

    Code frompackages/svelte/src/internal/client/runtime.js:

    exportfunctioncheck_dirtiness(reaction){// `reaction` is derived2// ... dependency loop runs ...if(dependency.wv>reaction.wv){// This is falsereturntrue;}// ...// This condition is met for derived2, so it gets marked CLEAN,// even though its dependency (derived1) is MAYBE_DIRTY.if(!is_unowned||(active_effect!==null&&!skip_reaction)){set_signal_status(reaction,CLEAN);}}

    This creates an inconsistent state:derived1 isMAYBE_DIRTY while its dependent,derived2, isCLEAN.

  3. Update Trigger: The Reactivity Chain is Broken

    Later, theoverride state is changed. This correctly callsmark_reactions on its dependencies, includingderived1.

    Code frompackages/svelte/src/internal/client/reactivity/sources.js:

    functionmark_reactions(signal,status){// ...for(/* ...reactions... */){var reaction=reactions[i];// This is derived1varflags=reaction.f;// `derived1` is now marked as DIRTY.set_signal_status(reaction,status);// This is where the chain breaks. Because `derived1` was MAYBE_DIRTY,// its `flags` do not contain the `CLEAN` bit. The condition fails,// and `mark_reactions` is never called on `derived2`.if((flags&(CLEAN|UNOWNED))!==0){if((flags&DERIVED)!==0){mark_reactions(/**@type {Derived} */(reaction),MAYBE_DIRTY);}// ...}}}

    Becausederived1 was stuck in theMAYBE_DIRTY state, the crucial check to continue the reaction chain fails.derived2 is never notified that it is stale.

  4. Result: Stale UI

    When Svelte flushes the effects to update the DOM, it sees thatderived2 is stillCLEAN and therefore does not re-render it. The UI is stuck showing the old value.

The Fix

The fix is to remove theskip_reaction check from the status calculation withinupdate_derived. This ensures a derived signal's status is determined only by its own properties (UNOWNED anddeps), preventing an unrelated context flag from corrupting its state.

Code change inpackages/svelte/src/internal/client/reactivity/deriveds.js:

// ...var status =-(skip_reaction || (derived.f & UNOWNED) !== 0) && derived.deps !== null ? MAYBE_DIRTY : CLEAN;+(derived.f & UNOWNED) !== 0 && derived.deps !== null ? MAYBE_DIRTY : CLEAN;set_signal_status(derived, status);// ...

This ensuresderived1 is correctly markedCLEAN in the first step. As a result, when its source changes, the(flags & CLEAN) !== 0 check inmark_reactions passes, the reactivity chain remains intact, andderived2 updates as expected.

Disclaimer

It is important to note that theskip_reaction flag has been part of this status calculation for a significant time. Therefore, while this fix does resolve the observed bug, I am not completely certain it is the ideal solution, as I do not fully understand the original intent behind includingskip_reaction in this specific logic. Removing the flag did not cause any existing tests to fail.

Furthermore, I was unable to create a test for this specific issue that fails without the fix and passes with it. The bug is consistently reproducible in a live browser environment, but it does not manifest in the JSDOM-based test runner.

Before submitting the PR, please make sure you do the following

  • It's really useful if your PR references an issue where it is discussed ahead of time. In many cases, features are absent for a reason. For large changes, please create an RFC:https://github.com/sveltejs/rfcs
  • Prefix your PR title withfeat:,fix:,chore:, ordocs:.
  • This message body should clearly illustrate what problems it solves.
  • Ideally, include a test that fails without this PR but passes with it.
  • If this PR changes code withinpackages/svelte/src, add a changeset (npx changeset).

Tests and linting

  • Run the tests withpnpm test and lint the project withpnpm lint

@github-actionsGitHub Actions
Copy link
Contributor

Playground

pnpm add https://pkg.pr.new/svelte@16111

@raythurnvoid
Copy link
ContributorAuthor

Fixed by:#16110 (comment)

Sign up for freeto join this conversation on GitHub. Already have an account?Sign in to comment
Reviewers
No reviews
Assignees
No one assigned
Labels
None yet
Projects
None yet
Milestone
No milestone
Development

Successfully merging this pull request may close these issues.

Derived value not receiving updates
1 participant
@raythurnvoid

[8]ページ先頭

©2009-2025 Movatter.jp