Repository: sveltejs/svelte (JavaScript)
Date: 2018-04-22 16:06:58+00:00
Task type: Bug Fix

Issue:
Component data not updated when firing an event as a result of an event fired in a nested component.
I have come across an issue when firing a custom event which is triggered as a result of an event fired in a nested component.

If you fire the custom event within the components directive, the data seems to be from the initial state of the container component. The value is changed within the nested component though.

If you use an event handler in the directive and fire the custom event from within the handler then the container components data is correct.

REPL example for a better explanation so ^ makes sense  - https://svelte.technology/repl?version=1.64.0&gist=60bdf43e8b114186d23cd85893295748


Issue discussion:
Looks like the issue was added in 1.56.0: https://svelte.technology/repl?version=1.55.1&gist=60bdf43e8b114186d23cd85893295748

Solution patch:
diff --git a/src/generators/nodes/Component.ts b/src/generators/nodes/Component.ts
--- a/src/generators/nodes/Component.ts
+++ b/src/generators/nodes/Component.ts
@@ -10,6 +10,7 @@ import mungeAttribute from './shared/mungeAttribute';
 import Node from './shared/Node';
 import Block from '../dom/Block';
 import Attribute from './Attribute';
+import usesThisOrArguments from '../../validate/js/utils/usesThisOrArguments';
 
 export default class Component extends Node {
 	type: 'Component';
@@ -494,8 +495,11 @@ function mungeEventHandler(generator: DomGenerator, node: Node, handler: Node, b
 				);
 			}
 
+		let usesState = false;
+
 		handler.expression.arguments.forEach((arg: Node) => {
 			const { contexts } = block.contextualise(arg, null, true);
+			if (contexts.has('state')) usesState = true;
 
 			contexts.forEach(context => {
 				allContexts.add(context);
@@ -503,11 +507,12 @@ function mungeEventHandler(generator: DomGenerator, node: Node, handler: Node, b
 		});
 
 		body = deindent`
+			${usesState && `const state = #component.get();`}
 			[✂${handler.expression.start}-${handler.expression.end}✂];
 		`;
 	} else {
 		body = deindent`
-			${block.alias('component')}.fire('${handler.name}', event);
+			#component.fire('${handler.name}', event);
 		`;
 	}
 


Test patch:
diff --git a/test/runtime/samples/component-event-not-stale/Button.html b/test/runtime/samples/component-event-not-stale/Button.html
new file mode 100644
--- /dev/null
+++ b/test/runtime/samples/component-event-not-stale/Button.html
@@ -0,0 +1 @@
+<button on:click><slot/></button>
\ No newline at end of file
diff --git a/test/runtime/samples/component-event-not-stale/_config.js b/test/runtime/samples/component-event-not-stale/_config.js
new file mode 100644
--- /dev/null
+++ b/test/runtime/samples/component-event-not-stale/_config.js
@@ -0,0 +1,30 @@
+export default {
+	data: {
+		value: 1,
+	},
+
+	test(assert, component, target, window) {
+		const buttons = target.querySelectorAll('button');
+		const click = new window.MouseEvent('click');
+
+		const events = [];
+		component.on('value', event => {
+			events.push(event);
+		});
+
+		buttons[0].dispatchEvent(click);
+		buttons[1].dispatchEvent(click);
+
+		component.set({ value: 2 });
+
+		buttons[0].dispatchEvent(click);
+		buttons[1].dispatchEvent(click);
+
+		assert.deepEqual(events, [
+			{ value: 1 },
+			{ value: 1 },
+			{ value: 2 },
+			{ value: 2 }
+		]);
+	},
+};
diff --git a/test/runtime/samples/component-event-not-stale/main.html b/test/runtime/samples/component-event-not-stale/main.html
new file mode 100644
--- /dev/null
+++ b/test/runtime/samples/component-event-not-stale/main.html
@@ -0,0 +1,20 @@
+<Button on:click="handleClick()">one</Button>
+<Button on:click="fire('value', {value})">two</Button>
+
+<script>
+	import Button from './Button.html';
+
+	export default {
+		components: {
+			Button
+		},
+
+		methods: {
+			handleClick() {
+				const { value } = this.get();
+				this.fire('value', { value });
+			}
+		}
+
+	}
+</script>
\ No newline at end of file