Repository: sveltejs/svelte (JavaScript)
Task type: Bug Fix

Issue:
Outros aren't correctly aborted
State is getting corrupted in this example:

![barchart-glitch](https://cloud.githubusercontent.com/assets/1162160/25595722/dbc946d2-2e93-11e7-8d9a-060a8d802cab.gif)

[REPL](https://svelte.technology/repl?version=1.18.2&gist=9dd404e533a3c2139aa328e3342b6a9d) (though it will just give a parse error until #525 is released)


Discussion:

Solution patch:
diff --git a/src/generators/dom/visitors/Element/addTransitions.js b/src/generators/dom/visitors/Element/addTransitions.js
--- a/src/generators/dom/visitors/Element/addTransitions.js
+++ b/src/generators/dom/visitors/Element/addTransitions.js
@@ -40,7 +40,10 @@ export default function addTransitions ( generator, block, state, node, intro, o
 			const fn = `${generator.alias( 'template' )}.transitions.${intro.name}`; // TODO add built-in transitions?
 
 			if ( outro ) {
-				block.builders.intro.addBlock( `if ( ${outroName} ) ${outroName}.abort();` );
+				block.builders.intro.addBlock( deindent`
+					if ( ${introName} ) ${introName}.abort();
+					if ( ${outroName} ) ${outroName}.abort();
+				` );
 			}
 
 			block.builders.intro.addBlock( deindent`
diff --git a/src/shared/transitions.js b/src/shared/transitions.js
--- a/src/shared/transitions.js
+++ b/src/shared/transitions.js
@@ -79,7 +79,7 @@ export function wrapTransition ( node, fn, params, intro, outgroup ) {
 			this.running = false;
 		},
 		abort: function () {
-			if ( !intro && obj.tick ) obj.tick( 1 ); // reset css for intro
+			if ( obj.tick ) obj.tick( 1 );
 			if ( obj.css ) document.head.removeChild( style );
 			this.running = false;
 		}


Test patch:
diff --git a/test/runtime/samples/transition-js-each-block-intro-outro/_config.js b/test/runtime/samples/transition-js-each-block-intro-outro/_config.js
new file mode 100644
--- /dev/null
+++ b/test/runtime/samples/transition-js-each-block-intro-outro/_config.js
@@ -0,0 +1,43 @@
+export default {
+	data: {
+		visible: false,
+		things: [ 'a', 'b', 'c' ]
+	},
+
+	test ( assert, component, target, window, raf ) {
+		component.set({ visible: true });
+		const divs = target.querySelectorAll( 'div' );
+		assert.equal( divs[0].foo, 0 );
+		assert.equal( divs[1].foo, 0 );
+		assert.equal( divs[2].foo, 0 );
+
+		raf.tick( 50 );
+		assert.equal( divs[0].foo, 0.5 );
+		assert.equal( divs[1].foo, 0.5 );
+		assert.equal( divs[2].foo, 0.5 );
+
+		component.set({ visible: false });
+
+		raf.tick( 70 );
+		assert.equal( divs[0].foo, 0.7 );
+		assert.equal( divs[1].foo, 0.7 );
+		assert.equal( divs[2].foo, 0.7 );
+
+		assert.equal( divs[0].bar, 0.8 );
+		assert.equal( divs[1].bar, 0.8 );
+		assert.equal( divs[2].bar, 0.8 );
+
+		component.set({ visible: true });
+
+		raf.tick( 100 );
+		assert.equal( divs[0].foo, 0.3 );
+		assert.equal( divs[1].foo, 0.3 );
+		assert.equal( divs[2].foo, 0.3 );
+
+		assert.equal( divs[0].bar, 1 );
+		assert.equal( divs[1].bar, 1 );
+		assert.equal( divs[2].bar, 1 );
+
+		component.destroy();
+	}
+};
\ No newline at end of file
diff --git a/test/runtime/samples/transition-js-each-block-intro-outro/main.html b/test/runtime/samples/transition-js-each-block-intro-outro/main.html
new file mode 100644
--- /dev/null
+++ b/test/runtime/samples/transition-js-each-block-intro-outro/main.html
@@ -0,0 +1,29 @@
+{{#each things as thing}}
+	{{#if visible}}
+		<div in:foo out:bar>{{thing}}</div>
+	{{/if}}
+{{/each}}
+
+<script>
+	export default {
+		transitions: {
+			foo: function ( node, params ) {
+				return {
+					duration: 100,
+					tick: t => {
+						node.foo = t;
+					}
+				};
+			},
+
+			bar: function ( node, params ) {
+				return {
+					duration: 100,
+					tick: t => {
+						node.bar = t;
+					}
+				};
+			}
+		}
+	};
+</script>
\ No newline at end of file