Skip to content

Conversation

@AyushCoder9
Copy link

… issues

Fixes #17227

When using destructuring in #each blocks (e.g., {#each gen() as [item]}), if the generator mutates the yielded object, all items would end up with the same final value because they all reference the same object.

This fix adds snapshotting for destructured patterns in each blocks, ensuring that each iteration gets a snapshot of the value at the time it was yielded, matching the behavior of a standard for...of loop.

  • Add snapshot_each_value, snapshot_array, and snapshot_object utilities
  • Apply snapshotting in both client and server EachBlock visitors
  • Add test case to prevent regression

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

… issues

Fixes sveltejs#17227

When using destructuring in #each blocks (e.g., {#each gen() as [item]}),
if the generator mutates the yielded object, all items would end up with
the same final value because they all reference the same object.

This fix adds snapshotting for destructured patterns in each blocks,
ensuring that each iteration gets a snapshot of the value at the time
it was yielded, matching the behavior of a standard for...of loop.

- Add snapshot_each_value, snapshot_array, and snapshot_object utilities
- Apply snapshotting in both client and server EachBlock visitors
- Add test case to prevent regression
@changeset-bot
Copy link

changeset-bot bot commented Nov 24, 2025

⚠️ No Changeset found

Latest commit: 8348132

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@Rich-Harris
Copy link
Member

Why would you want this behaviour? It's wrong. If you don't want the array to be mutated, don't mutate it.

@github-actions
Copy link
Contributor

Playground

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

…for...of behavior

Fixes sveltejs#17227

When using array destructuring in #each blocks (e.g., {#each gen() as [item]}),
if the generator mutates the yielded array object, all items would end up with
the same final value because destructuring happened after the collection was
converted to an array, rather than immediately during iteration.

This fix ensures that array destructuring happens immediately during iteration,
capturing values at the time they are yielded, matching the behavior of a
standard for...of loop with destructuring.

- Add to_array_destructured utility that destructures during iteration
- Apply immediate destructuring for array patterns in both client and server EachBlock visitors
- Keep object destructuring using snapshotting (shallow copy)
- Add test case to prevent regression
@svelte-docs-bot
Copy link

@AyushCoder9
Copy link
Author

Why would you want this behaviour? It's wrong. If you don't want the array to be mutated, don't mutate it.

I've updated the fix to match for...of behavior. Instead of snapshotting, array destructuring now happens immediately during iteration, capturing values at the time they are yielded.
Changes Made

  1. New to_array_destructured utility function
    Destructures array elements immediately during iteration (not after)
    When a generator yields the same array object multiple times (mutating it), we extract the values at iteration time
    Returns an array of arrays so extract_paths can process them correctly
  2. Updated EachBlock visitors (client & server)
    Detects when array destructuring patterns are used (e.g., [item])
    Calls to_array_destructured to destructure immediately during collection conversion
    This ensures each iteration gets the value at that moment, matching for...of behavior
  3. Object destructuring unchanged
    Object destructuring still uses snapshotting (shallow copy) since objects need to be copied
    Only array destructuring uses immediate destructuring
    How it works
    Before (incorrect behavior):
    // Generator yields same array object, mutating itgen() → [arr, arr, arr, arr, arr] // all reference same object// Later destructuring reads from same object → all get final value (4)
    After (matches for...of):
    // Destructure immediately during iterationgen() → extract [0] at yield 0, [1] at yield 1, etc.// Result: [0, 1, 2, 3, 4] captured at iteration time
    This matches for...of behavior where destructuring happens immediately, not by preventing mutation. The fix ensures values are captured at the time they're yielded, regardless of later mutations.
    Test case added to prevent regression. Ready for review.

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.

Unexpected #each block behavior with destructuring

2 participants