Skip to content
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 our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Array reactive: $state([]) and JS [] behave differently Array.push() #15505

Open
Fd929c2CE5fA opened this issue Mar 13, 2025 · 4 comments
Open

Comments

@Fd929c2CE5fA
Copy link

Describe the bug

In JS, Array.push() should preserve the original object reference.
But $state([]) does not seem to follow that logic.
This breaks the expected behavior of Svelte Array reactivity.

Reproduction

<script>
	let array = $state([[1,2]]);
	$inspect(array);
	let array2 = [[1,2]]; // JS Native Array for reference

	function click() {
		// order A
		const a = [3,4];
		array.push(a);
		array2.push(a);
		setTimeout(() => {
			a[1] = "AAA"; // "4" should change to "AAA"
			console.log(array2);
		}, 500);

		// order B
		array.push([5,6]);
		const b = array.at(-1);
		setTimeout(() => {
			b[1] = "BBB"; // "6" should change to "BBB"
		}, 500);
	}
	
</script>

<button onclick={click}>add</button>

{#each array as arr}
	<p>{arr[0]}/{arr[1]}</p>
{/each}

Playground

Logs

System Info

Svelte v5.20.4

Severity

annoyance

@trueadm
Copy link
Contributor

trueadm commented Mar 13, 2025

This is to be expected. When you push an element into a proxied array, then that too becomes a clone from the original and is also proxied. That means mutating the original backing array will not work. This works:

	function click() {
		// order A
		let a = [3,4];
		array.push(a);
		array2.push(a);
		a = array.at(-1); // we want to mutate the proxied version
		setTimeout(() => {
			a[1] = "AAA"; // "4" should change to "AAA"
			console.log(array2);
		}, 500);

		// order B
		array.push([5,6]);
		const b = array.at(-1);
		setTimeout(() => {
			b[1] = "BBB"; // "6" should change to "BBB"
		}, 500);
	}

@Fd929c2CE5fA
Copy link
Author

Fd929c2CE5fA commented Mar 14, 2025

Order B itself demonstrates a viable alternative.
You changed order A to the same behavior as order B, what is the point?

The logic here is that I first get the reference to the object and then push it into $state([]). Instead of pushing in and then looking from there.

The issue here is the difference in behavior between the Svelte proxied array and the original JS array.
If this is the expected behavior, I think documentation and warnings are necessary stuff.

This difference in behavior is not an inevitable consequence of the proxy or unavoidable. It ultimately depends on how Svelte implements the proxy and how the code compiles.

@trueadm
Copy link
Contributor

trueadm commented Mar 17, 2025

@Fd929c2CE5fA We just need to document this then. When you push an object into a proxied array, you're not actually pushing that object into the array. Under the hood, we're passing a copy of your array that has also been proxied into the array.

@Fd929c2CE5fA
Copy link
Author

Maybe the correct usage should be:

...
	function click() {
		// order A
-		const a = [3,4];
+		const a = $state([3,4]);
		array.push(a);
...

Playground

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants