fix: update indirectly-affected bindings on mutation

#16200

Pull request

Draft
R
Rich-Harris
Jun 18, 2025, 9:55 PM

WIP alternative to #16165. The real fix, I think, is to not use effects for synchronization at all, but rather to invalidate indirect bindings on mutation. In other words in a case like this...

<script>
  export let selected;
  export let tasks;
</script>

<select bind:value={selected}>
  {#each tasks as task}
    <option value='{task}'>{task.description}</option>
  {/each}
</select>

<label>
  <input type='checkbox' bind:checked={selected.done}> {selected.description}
</label>

<h2>Pending tasks</h2>
{#each tasks.filter(t => !t.done) as task}
  <p>{task.description}</p>
{/each}

...updating selected should also update tasks, because the bindings are linked. I think we might be able to use this mechanism for each blocks as well and end up with simpler compiler code, though I don't have time to finish it right now and my brain needs a rest anyway because this stuff is confusing as hell. Can't wait to be able to delete all this legacy gubbins.

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 with feat:, fix:, chore:, or docs:.
  • 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 within packages/svelte/src, add a changeset (npx changeset).

Tests and linting

  • Run the tests with pnpm test and lint the project with pnpm lint

Info

Assignees None
Reviewers None
Labels None
Milestone None