Repository: sveltejs/svelte (JavaScript)
Date: 2020-05-01 22:33:10+00:00
Task type: Bug Fix

Issue:
Compilation fails when using two bind:this to array/object with same key
Compilation fails when using two bind:this to array/object with same key
Error msg: `Variable 'xxx' already initialised with a different value`

Bug (version 3.20.1)
https://svelte.dev/repl/5d29d37523c14015b3d79fa53d21e1dd?version=3.20.1

Bug didn't exist in version 3.10.1 (haven't checked other versions tho):
https://svelte.dev/repl/5d29d37523c14015b3d79fa53d21e1dd?version=3.10.1

Note that this bug occurs when there is more than 1 component, if using 1 component it works.

Edit 1:
This bug occurs both when using destruction in `each` and without using it


Issue discussion:
I don't think it's a bug. Your variable is already binded to the DOM element corresponding to the first `Comp` component, so it can't be binded again to another DOM element. Why would you need to bind two elements to the same variable ?
@Oreilles 
I created a new REPL showing in some context what I am trying to achieve:

https://svelte.dev/repl/1e2c16c929a64890adeddf7fa9d65fa1?version=3.20.1

When using just one of the `<BootstrapToggle />` the code compiles fine
Oh, I missed that it was two different arrays `items1` and `items2` in your first example. Seems like a bug indeed.
The second REPL is far too complicated. This does however look like a bug. Very strange.
related https://github.com/sveltejs/svelte/issues/4636

Solution patch:
diff --git a/src/compiler/compile/render_dom/wrappers/shared/bind_this.ts b/src/compiler/compile/render_dom/wrappers/shared/bind_this.ts
--- a/src/compiler/compile/render_dom/wrappers/shared/bind_this.ts
+++ b/src/compiler/compile/render_dom/wrappers/shared/bind_this.ts
@@ -55,6 +55,9 @@ export default function bind_this(component: Component, block: Block, binding: B
 		const args = [];
 		for (const id of contextual_dependencies) {
 			args.push(id);
+			if (block.variables.has(id.name)) {
+				if (block.renderer.context_lookup.get(id.name).is_contextual) continue;
+			}
 			block.add_variable(id, block.renderer.reference(id.name));
 		}
 


Test patch:
diff --git a/test/runtime/samples/binding-this-each-object-props/_config.js b/test/runtime/samples/binding-this-each-object-props/_config.js
new file mode 100644
--- /dev/null
+++ b/test/runtime/samples/binding-this-each-object-props/_config.js
@@ -0,0 +1,14 @@
+export default {
+	html: ``,
+
+	async test({ assert, component, target }) {
+		component.visible = true;
+		assert.htmlEqual(target.innerHTML, `
+			<div>b</div><div>b</div><div>c</div><div>c</div>
+		`);
+		assert.equal(component.items1[1], target.querySelector('div'));
+		assert.equal(component.items2[1], target.querySelector('div:nth-child(2)'));
+		assert.equal(component.items1[2], target.querySelector('div:nth-child(3)'));
+		assert.equal(component.items2[2], target.querySelector('div:last-child'));
+	}
+};
diff --git a/test/runtime/samples/binding-this-each-object-props/main.svelte b/test/runtime/samples/binding-this-each-object-props/main.svelte
new file mode 100644
--- /dev/null
+++ b/test/runtime/samples/binding-this-each-object-props/main.svelte
@@ -0,0 +1,13 @@
+<script>
+	export const items1 = {};
+	export const items2 = {};
+	export let data = [
+		{id: 1, text: "b"},
+		{id: 2, text: "c"},
+	];
+</script>
+
+{#each data as item (item.id)}
+	<div bind:this={items1[item.id]}>{item.text}</div>
+	<div bind:this={items2[item.id]}>{item.text}</div>
+{/each}
diff --git a/test/runtime/samples/binding-this-each-object-spread/_config.js b/test/runtime/samples/binding-this-each-object-spread/_config.js
new file mode 100644
--- /dev/null
+++ b/test/runtime/samples/binding-this-each-object-spread/_config.js
@@ -0,0 +1,14 @@
+export default {
+	html: ``,
+
+	async test({ assert, component, target }) {
+		component.visible = true;
+		assert.htmlEqual(target.innerHTML, `
+			<div>a</div><div>a</div><div>b</div><div>b</div>
+		`);
+		assert.equal(component.items1[1], target.querySelector('div'));
+		assert.equal(component.items2[1], target.querySelector('div:nth-child(2)'));
+		assert.equal(component.items1[2], target.querySelector('div:nth-child(3)'));
+		assert.equal(component.items2[2], target.querySelector('div:last-child'));
+	}
+};
diff --git a/test/runtime/samples/binding-this-each-object-spread/main.svelte b/test/runtime/samples/binding-this-each-object-spread/main.svelte
new file mode 100644
--- /dev/null
+++ b/test/runtime/samples/binding-this-each-object-spread/main.svelte
@@ -0,0 +1,13 @@
+<script>
+	export const items1 = {};
+	export const items2 = {};
+	var data = [
+		{id: 1, text: "a"},
+		{id: 2, text: "b"},
+	];
+</script>
+
+{#each data as {id, text} (id)}
+	<div bind:this={items1[id]}>{text}</div>
+	<div bind:this={items2[id]}>{text}</div>
+{/each}