From 3330c3fbab56c1713a0f096922e288e395059eb9 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 26 May 2020 18:56:15 -0400 Subject: [PATCH] only add event listeners when a block is first mounted (#4860) --- CHANGELOG.md | 1 + src/compiler/compile/render_dom/Block.ts | 21 +++++++---- src/runtime/internal/keyed_each.ts | 2 +- .../action-custom-event-handler/expected.js | 11 ++++-- test/js/samples/action/expected.js | 11 ++++-- test/js/samples/bind-online/expected.js | 18 ++++++---- test/js/samples/bind-open/expected.js | 11 ++++-- .../bindings-readonly-order/expected.js | 17 +++++---- .../capture-inject-dev-only/expected.js | 11 ++++-- .../samples/component-static-var/expected.js | 11 ++++-- .../expected.js | 11 ++++-- .../samples/dont-invalidate-this/expected.js | 11 ++++-- .../samples/event-handler-dynamic/expected.js | 23 +++++++----- .../event-handler-no-passive/expected.js | 11 ++++-- test/js/samples/event-modifiers/expected.js | 21 ++++++----- test/js/samples/input-files/expected.js | 11 ++++-- .../input-no-initial-value/expected.js | 17 +++++---- test/js/samples/input-range/expected.js | 17 +++++---- test/js/samples/input-value/expected.js | 11 ++++-- .../input-without-blowback-guard/expected.js | 11 ++++-- .../expected.js | 11 ++++-- .../expected.js | 11 ++++-- .../expected.js | 11 ++++-- .../expected.js | 11 ++++-- test/js/samples/media-bindings/expected.js | 36 ++++++++++--------- test/js/samples/video-bindings/expected.js | 17 +++++---- .../samples/window-binding-online/expected.js | 18 ++++++---- .../samples/window-binding-scroll/expected.js | 21 ++++++----- .../Component.svelte | 5 +++ .../_config.js | 21 +++++++++++ .../main.svelte | 17 +++++++++ 31 files changed, 305 insertions(+), 132 deletions(-) create mode 100644 test/runtime/samples/each-block-keyed-component-action/Component.svelte create mode 100644 test/runtime/samples/each-block-keyed-component-action/_config.js create mode 100644 test/runtime/samples/each-block-keyed-component-action/main.svelte diff --git a/CHANGELOG.md b/CHANGELOG.md index 07635690d3..3bc60ecde3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ * Add `a11y-no-onchange` warning ([#4788](https://github.com/sveltejs/svelte/pull/4788)) * Add `muted` binding for media elements ([#2998](https://github.com/sveltejs/svelte/issues/2998)) * Fix let-less `` with context overflow ([#4624](https://github.com/sveltejs/svelte/issues/4624)) +* Fix `use:` actions being recreated when a keyed `{#each}` is reordered ([#4693](https://github.com/sveltejs/svelte/issues/4693)) ## 3.22.3 diff --git a/src/compiler/compile/render_dom/Block.ts b/src/compiler/compile/render_dom/Block.ts index b29b4d4afd..b77cf61112 100644 --- a/src/compiler/compile/render_dom/Block.ts +++ b/src/compiler/compile/render_dom/Block.ts @@ -299,7 +299,7 @@ export default class Block { ${this.chunks.mount} }`; } else { - properties.mount = x`function #mount(#target, #anchor, #remount) { + properties.mount = x`function #mount(#target, #anchor) { ${this.chunks.mount} }`; } @@ -452,6 +452,9 @@ export default class Block { render_listeners(chunk: string = '') { if (this.event_listeners.length > 0) { + this.add_variable({ type: 'Identifier', name: '#mounted' }); + this.chunks.destroy.push(b`#mounted = false`); + const dispose: Identifier = { type: 'Identifier', name: `#dispose${chunk}` @@ -462,8 +465,10 @@ export default class Block { if (this.event_listeners.length === 1) { this.chunks.mount.push( b` - if (#remount) ${dispose}(); - ${dispose} = ${this.event_listeners[0]}; + if (!#mounted) { + ${dispose} = ${this.event_listeners[0]}; + #mounted = true; + } ` ); @@ -472,10 +477,12 @@ export default class Block { ); } else { this.chunks.mount.push(b` - if (#remount) @run_all(${dispose}); - ${dispose} = [ - ${this.event_listeners} - ]; + if (!#mounted) { + ${dispose} = [ + ${this.event_listeners} + ]; + #mounted = true; + } `); this.chunks.destroy.push( diff --git a/src/runtime/internal/keyed_each.ts b/src/runtime/internal/keyed_each.ts index 6b56010d46..b397335c87 100644 --- a/src/runtime/internal/keyed_each.ts +++ b/src/runtime/internal/keyed_each.ts @@ -56,7 +56,7 @@ export function update_keyed_each(old_blocks, dirty, get_key, dynamic, ctx, list function insert(block) { transition_in(block, 1); - block.m(node, next, lookup.has(block.key)); + block.m(node, next); lookup.set(block.key, block); next = block.first; n--; diff --git a/test/js/samples/action-custom-event-handler/expected.js b/test/js/samples/action-custom-event-handler/expected.js index aa2941f404..cac2f61b44 100644 --- a/test/js/samples/action-custom-event-handler/expected.js +++ b/test/js/samples/action-custom-event-handler/expected.js @@ -14,6 +14,7 @@ import { function create_fragment(ctx) { let button; let foo_action; + let mounted; let dispose; return { @@ -21,10 +22,13 @@ function create_fragment(ctx) { button = element("button"); button.textContent = "foo"; }, - m(target, anchor, remount) { + m(target, anchor) { insert(target, button, anchor); - if (remount) dispose(); - dispose = action_destroyer(foo_action = foo.call(null, button, /*foo_function*/ ctx[1])); + + if (!mounted) { + dispose = action_destroyer(foo_action = foo.call(null, button, /*foo_function*/ ctx[1])); + mounted = true; + } }, p(ctx, [dirty]) { if (foo_action && is_function(foo_action.update) && dirty & /*bar*/ 1) foo_action.update.call(null, /*foo_function*/ ctx[1]); @@ -33,6 +37,7 @@ function create_fragment(ctx) { o: noop, d(detaching) { if (detaching) detach(button); + mounted = false; dispose(); } }; diff --git a/test/js/samples/action/expected.js b/test/js/samples/action/expected.js index 1b3f5cc945..d52960bddd 100644 --- a/test/js/samples/action/expected.js +++ b/test/js/samples/action/expected.js @@ -14,6 +14,7 @@ import { function create_fragment(ctx) { let a; let link_action; + let mounted; let dispose; return { @@ -22,16 +23,20 @@ function create_fragment(ctx) { a.textContent = "Test"; attr(a, "href", "#"); }, - m(target, anchor, remount) { + m(target, anchor) { insert(target, a, anchor); - if (remount) dispose(); - dispose = action_destroyer(link_action = link.call(null, a)); + + if (!mounted) { + dispose = action_destroyer(link_action = link.call(null, a)); + mounted = true; + } }, p: noop, i: noop, o: noop, d(detaching) { if (detaching) detach(a); + mounted = false; dispose(); } }; diff --git a/test/js/samples/bind-online/expected.js b/test/js/samples/bind-online/expected.js index fa955e4fd5..887195bce1 100644 --- a/test/js/samples/bind-online/expected.js +++ b/test/js/samples/bind-online/expected.js @@ -10,23 +10,27 @@ import { } from "svelte/internal"; function create_fragment(ctx) { + let mounted; let dispose; add_render_callback(/*onlinestatuschanged*/ ctx[1]); return { c: noop, - m(target, anchor, remount) { - if (remount) run_all(dispose); - - dispose = [ - listen(window, "online", /*onlinestatuschanged*/ ctx[1]), - listen(window, "offline", /*onlinestatuschanged*/ ctx[1]) - ]; + m(target, anchor) { + if (!mounted) { + dispose = [ + listen(window, "online", /*onlinestatuschanged*/ ctx[1]), + listen(window, "offline", /*onlinestatuschanged*/ ctx[1]) + ]; + + mounted = true; + } }, p: noop, i: noop, o: noop, d(detaching) { + mounted = false; run_all(dispose); } }; diff --git a/test/js/samples/bind-open/expected.js b/test/js/samples/bind-open/expected.js index 77b68f1b27..f600157c65 100644 --- a/test/js/samples/bind-open/expected.js +++ b/test/js/samples/bind-open/expected.js @@ -12,6 +12,7 @@ import { function create_fragment(ctx) { let details; + let mounted; let dispose; return { @@ -21,11 +22,14 @@ function create_fragment(ctx) { details.innerHTML = `summarycontent `; }, - m(target, anchor, remount) { + m(target, anchor) { insert(target, details, anchor); details.open = /*open*/ ctx[0]; - if (remount) dispose(); - dispose = listen(details, "toggle", /*details_toggle_handler*/ ctx[1]); + + if (!mounted) { + dispose = listen(details, "toggle", /*details_toggle_handler*/ ctx[1]); + mounted = true; + } }, p(ctx, [dirty]) { if (dirty & /*open*/ 1) { @@ -36,6 +40,7 @@ function create_fragment(ctx) { o: noop, d(detaching) { if (detaching) detach(details); + mounted = false; dispose(); } }; diff --git a/test/js/samples/bindings-readonly-order/expected.js b/test/js/samples/bindings-readonly-order/expected.js index 00e8a5bf00..0e845c65b8 100644 --- a/test/js/samples/bindings-readonly-order/expected.js +++ b/test/js/samples/bindings-readonly-order/expected.js @@ -17,6 +17,7 @@ function create_fragment(ctx) { let input0; let t; let input1; + let mounted; let dispose; return { @@ -27,16 +28,19 @@ function create_fragment(ctx) { attr(input0, "type", "file"); attr(input1, "type", "file"); }, - m(target, anchor, remount) { + m(target, anchor) { insert(target, input0, anchor); insert(target, t, anchor); insert(target, input1, anchor); - if (remount) run_all(dispose); - dispose = [ - listen(input0, "change", /*input0_change_handler*/ ctx[1]), - listen(input1, "change", /*input1_change_handler*/ ctx[2]) - ]; + if (!mounted) { + dispose = [ + listen(input0, "change", /*input0_change_handler*/ ctx[1]), + listen(input1, "change", /*input1_change_handler*/ ctx[2]) + ]; + + mounted = true; + } }, p: noop, i: noop, @@ -45,6 +49,7 @@ function create_fragment(ctx) { if (detaching) detach(input0); if (detaching) detach(t); if (detaching) detach(input1); + mounted = false; run_all(dispose); } }; diff --git a/test/js/samples/capture-inject-dev-only/expected.js b/test/js/samples/capture-inject-dev-only/expected.js index 3e355e1491..a87693dd9e 100644 --- a/test/js/samples/capture-inject-dev-only/expected.js +++ b/test/js/samples/capture-inject-dev-only/expected.js @@ -20,6 +20,7 @@ function create_fragment(ctx) { let t0; let t1; let input; + let mounted; let dispose; return { @@ -29,14 +30,17 @@ function create_fragment(ctx) { t1 = space(); input = element("input"); }, - m(target, anchor, remount) { + m(target, anchor) { insert(target, p, anchor); append(p, t0); insert(target, t1, anchor); insert(target, input, anchor); set_input_value(input, /*foo*/ ctx[0]); - if (remount) dispose(); - dispose = listen(input, "input", /*input_input_handler*/ ctx[1]); + + if (!mounted) { + dispose = listen(input, "input", /*input_input_handler*/ ctx[1]); + mounted = true; + } }, p(ctx, [dirty]) { if (dirty & /*foo*/ 1) set_data(t0, /*foo*/ ctx[0]); @@ -51,6 +55,7 @@ function create_fragment(ctx) { if (detaching) detach(p); if (detaching) detach(t1); if (detaching) detach(input); + mounted = false; dispose(); } }; diff --git a/test/js/samples/component-static-var/expected.js b/test/js/samples/component-static-var/expected.js index c032a0636d..8a27ab6b24 100644 --- a/test/js/samples/component-static-var/expected.js +++ b/test/js/samples/component-static-var/expected.js @@ -24,6 +24,7 @@ function create_fragment(ctx) { let t1; let input; let current; + let mounted; let dispose; const foo = new Foo({ props: { x: y } }); const bar = new Bar({ props: { x: /*z*/ ctx[0] } }); @@ -36,7 +37,7 @@ function create_fragment(ctx) { t1 = space(); input = element("input"); }, - m(target, anchor, remount) { + m(target, anchor) { mount_component(foo, target, anchor); insert(target, t0, anchor); mount_component(bar, target, anchor); @@ -44,8 +45,11 @@ function create_fragment(ctx) { insert(target, input, anchor); set_input_value(input, /*z*/ ctx[0]); current = true; - if (remount) dispose(); - dispose = listen(input, "input", /*input_input_handler*/ ctx[1]); + + if (!mounted) { + dispose = listen(input, "input", /*input_input_handler*/ ctx[1]); + mounted = true; + } }, p(ctx, [dirty]) { const bar_changes = {}; @@ -73,6 +77,7 @@ function create_fragment(ctx) { destroy_component(bar, detaching); if (detaching) detach(t1); if (detaching) detach(input); + mounted = false; dispose(); } }; diff --git a/test/js/samples/component-store-reassign-invalidate/expected.js b/test/js/samples/component-store-reassign-invalidate/expected.js index b33047d8f3..0cf555bf0d 100644 --- a/test/js/samples/component-store-reassign-invalidate/expected.js +++ b/test/js/samples/component-store-reassign-invalidate/expected.js @@ -22,6 +22,7 @@ function create_fragment(ctx) { let t0; let t1; let button; + let mounted; let dispose; return { @@ -32,13 +33,16 @@ function create_fragment(ctx) { button = element("button"); button.textContent = "reset"; }, - m(target, anchor, remount) { + m(target, anchor) { insert(target, h1, anchor); append(h1, t0); insert(target, t1, anchor); insert(target, button, anchor); - if (remount) dispose(); - dispose = listen(button, "click", /*click_handler*/ ctx[2]); + + if (!mounted) { + dispose = listen(button, "click", /*click_handler*/ ctx[2]); + mounted = true; + } }, p(ctx, [dirty]) { if (dirty & /*$foo*/ 2) set_data(t0, /*$foo*/ ctx[1]); @@ -49,6 +53,7 @@ function create_fragment(ctx) { if (detaching) detach(h1); if (detaching) detach(t1); if (detaching) detach(button); + mounted = false; dispose(); } }; diff --git a/test/js/samples/dont-invalidate-this/expected.js b/test/js/samples/dont-invalidate-this/expected.js index ba9e7152d7..0b155373a1 100644 --- a/test/js/samples/dont-invalidate-this/expected.js +++ b/test/js/samples/dont-invalidate-this/expected.js @@ -12,22 +12,27 @@ import { function create_fragment(ctx) { let input; + let mounted; let dispose; return { c() { input = element("input"); }, - m(target, anchor, remount) { + m(target, anchor) { insert(target, input, anchor); - if (remount) dispose(); - dispose = listen(input, "input", make_uppercase); + + if (!mounted) { + dispose = listen(input, "input", make_uppercase); + mounted = true; + } }, p: noop, i: noop, o: noop, d(detaching) { if (detaching) detach(input); + mounted = false; dispose(); } }; diff --git a/test/js/samples/event-handler-dynamic/expected.js b/test/js/samples/event-handler-dynamic/expected.js index d8f5710023..dacdfe15ac 100644 --- a/test/js/samples/event-handler-dynamic/expected.js +++ b/test/js/samples/event-handler-dynamic/expected.js @@ -26,6 +26,7 @@ function create_fragment(ctx) { let t4; let t5; let button2; + let mounted; let dispose; return { @@ -43,7 +44,7 @@ function create_fragment(ctx) { button2 = element("button"); button2.textContent = "click"; }, - m(target, anchor, remount) { + m(target, anchor) { insert(target, p0, anchor); append(p0, button0); append(p0, t1); @@ -53,15 +54,18 @@ function create_fragment(ctx) { append(p1, t4); insert(target, t5, anchor); insert(target, button2, anchor); - if (remount) run_all(dispose); - dispose = [ - listen(button0, "click", /*updateHandler1*/ ctx[2]), - listen(button1, "click", /*updateHandler2*/ ctx[3]), - listen(button2, "click", function () { - if (is_function(/*clickHandler*/ ctx[0])) /*clickHandler*/ ctx[0].apply(this, arguments); - }) - ]; + if (!mounted) { + dispose = [ + listen(button0, "click", /*updateHandler1*/ ctx[2]), + listen(button1, "click", /*updateHandler2*/ ctx[3]), + listen(button2, "click", function () { + if (is_function(/*clickHandler*/ ctx[0])) /*clickHandler*/ ctx[0].apply(this, arguments); + }) + ]; + + mounted = true; + } }, p(new_ctx, [dirty]) { ctx = new_ctx; @@ -75,6 +79,7 @@ function create_fragment(ctx) { if (detaching) detach(p1); if (detaching) detach(t5); if (detaching) detach(button2); + mounted = false; run_all(dispose); } }; diff --git a/test/js/samples/event-handler-no-passive/expected.js b/test/js/samples/event-handler-no-passive/expected.js index 8bf2de7693..4d4b910d4e 100644 --- a/test/js/samples/event-handler-no-passive/expected.js +++ b/test/js/samples/event-handler-no-passive/expected.js @@ -13,6 +13,7 @@ import { function create_fragment(ctx) { let a; + let mounted; let dispose; return { @@ -21,16 +22,20 @@ function create_fragment(ctx) { a.textContent = "this should not navigate to example.com"; attr(a, "href", "https://example.com"); }, - m(target, anchor, remount) { + m(target, anchor) { insert(target, a, anchor); - if (remount) dispose(); - dispose = listen(a, "touchstart", touchstart_handler); + + if (!mounted) { + dispose = listen(a, "touchstart", touchstart_handler); + mounted = true; + } }, p: noop, i: noop, o: noop, d(detaching) { if (detaching) detach(a); + mounted = false; dispose(); } }; diff --git a/test/js/samples/event-modifiers/expected.js b/test/js/samples/event-modifiers/expected.js index 2eca5f29b8..6aa3c161f9 100644 --- a/test/js/samples/event-modifiers/expected.js +++ b/test/js/samples/event-modifiers/expected.js @@ -22,6 +22,7 @@ function create_fragment(ctx) { let button1; let t3; let button2; + let mounted; let dispose; return { @@ -36,27 +37,31 @@ function create_fragment(ctx) { button2 = element("button"); button2.textContent = "or me!"; }, - m(target, anchor, remount) { + m(target, anchor) { insert(target, div, anchor); append(div, button0); append(div, t1); append(div, button1); append(div, t3); append(div, button2); - if (remount) run_all(dispose); - dispose = [ - listen(button0, "click", stop_propagation(prevent_default(handleClick))), - listen(button1, "click", handleClick, { once: true, capture: true }), - listen(button2, "click", handleClick, true), - listen(div, "touchstart", handleTouchstart, { passive: true }) - ]; + if (!mounted) { + dispose = [ + listen(button0, "click", stop_propagation(prevent_default(handleClick))), + listen(button1, "click", handleClick, { once: true, capture: true }), + listen(button2, "click", handleClick, true), + listen(div, "touchstart", handleTouchstart, { passive: true }) + ]; + + mounted = true; + } }, p: noop, i: noop, o: noop, d(detaching) { if (detaching) detach(div); + mounted = false; run_all(dispose); } }; diff --git a/test/js/samples/input-files/expected.js b/test/js/samples/input-files/expected.js index 1c1e57fc9b..0069c2e5f8 100644 --- a/test/js/samples/input-files/expected.js +++ b/test/js/samples/input-files/expected.js @@ -13,6 +13,7 @@ import { function create_fragment(ctx) { let input; + let mounted; let dispose; return { @@ -21,16 +22,20 @@ function create_fragment(ctx) { attr(input, "type", "file"); input.multiple = true; }, - m(target, anchor, remount) { + m(target, anchor) { insert(target, input, anchor); - if (remount) dispose(); - dispose = listen(input, "change", /*input_change_handler*/ ctx[1]); + + if (!mounted) { + dispose = listen(input, "change", /*input_change_handler*/ ctx[1]); + mounted = true; + } }, p: noop, i: noop, o: noop, d(detaching) { if (detaching) detach(input); + mounted = false; dispose(); } }; diff --git a/test/js/samples/input-no-initial-value/expected.js b/test/js/samples/input-no-initial-value/expected.js index f72daa22a3..3354aa3111 100644 --- a/test/js/samples/input-no-initial-value/expected.js +++ b/test/js/samples/input-no-initial-value/expected.js @@ -20,6 +20,7 @@ function create_fragment(ctx) { let input; let t0; let button; + let mounted; let dispose; return { @@ -32,18 +33,21 @@ function create_fragment(ctx) { attr(input, "type", "text"); input.required = true; }, - m(target, anchor, remount) { + m(target, anchor) { insert(target, form, anchor); append(form, input); set_input_value(input, /*test*/ ctx[0]); append(form, t0); append(form, button); - if (remount) run_all(dispose); - dispose = [ - listen(input, "input", /*input_input_handler*/ ctx[2]), - listen(form, "submit", /*handleSubmit*/ ctx[1]) - ]; + if (!mounted) { + dispose = [ + listen(input, "input", /*input_input_handler*/ ctx[2]), + listen(form, "submit", /*handleSubmit*/ ctx[1]) + ]; + + mounted = true; + } }, p(ctx, [dirty]) { if (dirty & /*test*/ 1 && input.value !== /*test*/ ctx[0]) { @@ -54,6 +58,7 @@ function create_fragment(ctx) { o: noop, d(detaching) { if (detaching) detach(form); + mounted = false; run_all(dispose); } }; diff --git a/test/js/samples/input-range/expected.js b/test/js/samples/input-range/expected.js index 17a8065449..770baa29ed 100644 --- a/test/js/samples/input-range/expected.js +++ b/test/js/samples/input-range/expected.js @@ -16,6 +16,7 @@ import { function create_fragment(ctx) { let input; + let mounted; let dispose; return { @@ -23,15 +24,18 @@ function create_fragment(ctx) { input = element("input"); attr(input, "type", "range"); }, - m(target, anchor, remount) { + m(target, anchor) { insert(target, input, anchor); set_input_value(input, /*value*/ ctx[0]); - if (remount) run_all(dispose); - dispose = [ - listen(input, "change", /*input_change_input_handler*/ ctx[1]), - listen(input, "input", /*input_change_input_handler*/ ctx[1]) - ]; + if (!mounted) { + dispose = [ + listen(input, "change", /*input_change_input_handler*/ ctx[1]), + listen(input, "input", /*input_change_input_handler*/ ctx[1]) + ]; + + mounted = true; + } }, p(ctx, [dirty]) { if (dirty & /*value*/ 1) { @@ -42,6 +46,7 @@ function create_fragment(ctx) { o: noop, d(detaching) { if (detaching) detach(input); + mounted = false; run_all(dispose); } }; diff --git a/test/js/samples/input-value/expected.js b/test/js/samples/input-value/expected.js index 31be2895ac..781a2ae603 100644 --- a/test/js/samples/input-value/expected.js +++ b/test/js/samples/input-value/expected.js @@ -20,6 +20,7 @@ function create_fragment(ctx) { let h1; let t1; let t2; + let mounted; let dispose; return { @@ -31,14 +32,17 @@ function create_fragment(ctx) { t2 = text("!"); input.value = /*name*/ ctx[0]; }, - m(target, anchor, remount) { + m(target, anchor) { insert(target, input, anchor); insert(target, t0, anchor); insert(target, h1, anchor); append(h1, t1); append(h1, t2); - if (remount) dispose(); - dispose = listen(input, "input", /*onInput*/ ctx[1]); + + if (!mounted) { + dispose = listen(input, "input", /*onInput*/ ctx[1]); + mounted = true; + } }, p(ctx, [dirty]) { if (dirty & /*name*/ 1 && input.value !== /*name*/ ctx[0]) { @@ -53,6 +57,7 @@ function create_fragment(ctx) { if (detaching) detach(input); if (detaching) detach(t0); if (detaching) detach(h1); + mounted = false; dispose(); } }; diff --git a/test/js/samples/input-without-blowback-guard/expected.js b/test/js/samples/input-without-blowback-guard/expected.js index 1e379032f3..f19f74dc1e 100644 --- a/test/js/samples/input-without-blowback-guard/expected.js +++ b/test/js/samples/input-without-blowback-guard/expected.js @@ -13,6 +13,7 @@ import { function create_fragment(ctx) { let input; + let mounted; let dispose; return { @@ -20,11 +21,14 @@ function create_fragment(ctx) { input = element("input"); attr(input, "type", "checkbox"); }, - m(target, anchor, remount) { + m(target, anchor) { insert(target, input, anchor); input.checked = /*foo*/ ctx[0]; - if (remount) dispose(); - dispose = listen(input, "change", /*input_change_handler*/ ctx[1]); + + if (!mounted) { + dispose = listen(input, "change", /*input_change_handler*/ ctx[1]); + mounted = true; + } }, p(ctx, [dirty]) { if (dirty & /*foo*/ 1) { @@ -35,6 +39,7 @@ function create_fragment(ctx) { o: noop, d(detaching) { if (detaching) detach(input); + mounted = false; dispose(); } }; diff --git a/test/js/samples/instrumentation-script-if-no-block/expected.js b/test/js/samples/instrumentation-script-if-no-block/expected.js index f45e52aebd..7f51c64550 100644 --- a/test/js/samples/instrumentation-script-if-no-block/expected.js +++ b/test/js/samples/instrumentation-script-if-no-block/expected.js @@ -20,6 +20,7 @@ function create_fragment(ctx) { let p; let t2; let t3; + let mounted; let dispose; return { @@ -31,14 +32,17 @@ function create_fragment(ctx) { t2 = text("x: "); t3 = text(/*x*/ ctx[0]); }, - m(target, anchor, remount) { + m(target, anchor) { insert(target, button, anchor); insert(target, t1, anchor); insert(target, p, anchor); append(p, t2); append(p, t3); - if (remount) dispose(); - dispose = listen(button, "click", /*foo*/ ctx[1]); + + if (!mounted) { + dispose = listen(button, "click", /*foo*/ ctx[1]); + mounted = true; + } }, p(ctx, [dirty]) { if (dirty & /*x*/ 1) set_data(t3, /*x*/ ctx[0]); @@ -49,6 +53,7 @@ function create_fragment(ctx) { if (detaching) detach(button); if (detaching) detach(t1); if (detaching) detach(p); + mounted = false; dispose(); } }; diff --git a/test/js/samples/instrumentation-script-x-equals-x/expected.js b/test/js/samples/instrumentation-script-x-equals-x/expected.js index 9fe9640978..ea0d65acb9 100644 --- a/test/js/samples/instrumentation-script-x-equals-x/expected.js +++ b/test/js/samples/instrumentation-script-x-equals-x/expected.js @@ -21,6 +21,7 @@ function create_fragment(ctx) { let t2; let t3_value = /*things*/ ctx[0].length + ""; let t3; + let mounted; let dispose; return { @@ -32,14 +33,17 @@ function create_fragment(ctx) { t2 = text("number of things: "); t3 = text(t3_value); }, - m(target, anchor, remount) { + m(target, anchor) { insert(target, button, anchor); insert(target, t1, anchor); insert(target, p, anchor); append(p, t2); append(p, t3); - if (remount) dispose(); - dispose = listen(button, "click", /*foo*/ ctx[1]); + + if (!mounted) { + dispose = listen(button, "click", /*foo*/ ctx[1]); + mounted = true; + } }, p(ctx, [dirty]) { if (dirty & /*things*/ 1 && t3_value !== (t3_value = /*things*/ ctx[0].length + "")) set_data(t3, t3_value); @@ -50,6 +54,7 @@ function create_fragment(ctx) { if (detaching) detach(button); if (detaching) detach(t1); if (detaching) detach(p); + mounted = false; dispose(); } }; diff --git a/test/js/samples/instrumentation-template-if-no-block/expected.js b/test/js/samples/instrumentation-template-if-no-block/expected.js index 6f3bea4010..0c2713a9cc 100644 --- a/test/js/samples/instrumentation-template-if-no-block/expected.js +++ b/test/js/samples/instrumentation-template-if-no-block/expected.js @@ -20,6 +20,7 @@ function create_fragment(ctx) { let p; let t2; let t3; + let mounted; let dispose; return { @@ -31,14 +32,17 @@ function create_fragment(ctx) { t2 = text("x: "); t3 = text(/*x*/ ctx[0]); }, - m(target, anchor, remount) { + m(target, anchor) { insert(target, button, anchor); insert(target, t1, anchor); insert(target, p, anchor); append(p, t2); append(p, t3); - if (remount) dispose(); - dispose = listen(button, "click", /*click_handler*/ ctx[1]); + + if (!mounted) { + dispose = listen(button, "click", /*click_handler*/ ctx[1]); + mounted = true; + } }, p(ctx, [dirty]) { if (dirty & /*x*/ 1) set_data(t3, /*x*/ ctx[0]); @@ -49,6 +53,7 @@ function create_fragment(ctx) { if (detaching) detach(button); if (detaching) detach(t1); if (detaching) detach(p); + mounted = false; dispose(); } }; diff --git a/test/js/samples/instrumentation-template-x-equals-x/expected.js b/test/js/samples/instrumentation-template-x-equals-x/expected.js index ed095353bd..55cd310661 100644 --- a/test/js/samples/instrumentation-template-x-equals-x/expected.js +++ b/test/js/samples/instrumentation-template-x-equals-x/expected.js @@ -21,6 +21,7 @@ function create_fragment(ctx) { let t2; let t3_value = /*things*/ ctx[0].length + ""; let t3; + let mounted; let dispose; return { @@ -32,14 +33,17 @@ function create_fragment(ctx) { t2 = text("number of things: "); t3 = text(t3_value); }, - m(target, anchor, remount) { + m(target, anchor) { insert(target, button, anchor); insert(target, t1, anchor); insert(target, p, anchor); append(p, t2); append(p, t3); - if (remount) dispose(); - dispose = listen(button, "click", /*click_handler*/ ctx[1]); + + if (!mounted) { + dispose = listen(button, "click", /*click_handler*/ ctx[1]); + mounted = true; + } }, p(ctx, [dirty]) { if (dirty & /*things*/ 1 && t3_value !== (t3_value = /*things*/ ctx[0].length + "")) set_data(t3, t3_value); @@ -50,6 +54,7 @@ function create_fragment(ctx) { if (detaching) detach(button); if (detaching) detach(t1); if (detaching) detach(p); + mounted = false; dispose(); } }; diff --git a/test/js/samples/media-bindings/expected.js b/test/js/samples/media-bindings/expected.js index 0849a38b51..bcbc0647b8 100644 --- a/test/js/samples/media-bindings/expected.js +++ b/test/js/samples/media-bindings/expected.js @@ -19,6 +19,7 @@ function create_fragment(ctx) { let audio_updating = false; let audio_animationframe; let audio_is_paused = true; + let mounted; let dispose; function audio_timeupdate_handler() { @@ -42,7 +43,7 @@ function create_fragment(ctx) { if (/*seeking*/ ctx[9] === void 0) add_render_callback(() => /*audio_seeking_seeked_handler*/ ctx[18].call(audio)); if (/*ended*/ ctx[10] === void 0) add_render_callback(() => /*audio_ended_handler*/ ctx[19].call(audio)); }, - m(target, anchor, remount) { + m(target, anchor) { insert(target, audio, anchor); if (!isNaN(/*volume*/ ctx[6])) { @@ -55,21 +56,23 @@ function create_fragment(ctx) { audio.playbackRate = /*playbackRate*/ ctx[8]; } - if (remount) run_all(dispose); - - dispose = [ - listen(audio, "progress", /*audio_progress_handler*/ ctx[11]), - listen(audio, "loadedmetadata", /*audio_loadedmetadata_handler*/ ctx[12]), - listen(audio, "timeupdate", audio_timeupdate_handler), - listen(audio, "durationchange", /*audio_durationchange_handler*/ ctx[14]), - listen(audio, "play", /*audio_play_pause_handler*/ ctx[15]), - listen(audio, "pause", /*audio_play_pause_handler*/ ctx[15]), - listen(audio, "volumechange", /*audio_volumechange_handler*/ ctx[16]), - listen(audio, "ratechange", /*audio_ratechange_handler*/ ctx[17]), - listen(audio, "seeking", /*audio_seeking_seeked_handler*/ ctx[18]), - listen(audio, "seeked", /*audio_seeking_seeked_handler*/ ctx[18]), - listen(audio, "ended", /*audio_ended_handler*/ ctx[19]) - ]; + if (!mounted) { + dispose = [ + listen(audio, "progress", /*audio_progress_handler*/ ctx[11]), + listen(audio, "loadedmetadata", /*audio_loadedmetadata_handler*/ ctx[12]), + listen(audio, "timeupdate", audio_timeupdate_handler), + listen(audio, "durationchange", /*audio_durationchange_handler*/ ctx[14]), + listen(audio, "play", /*audio_play_pause_handler*/ ctx[15]), + listen(audio, "pause", /*audio_play_pause_handler*/ ctx[15]), + listen(audio, "volumechange", /*audio_volumechange_handler*/ ctx[16]), + listen(audio, "ratechange", /*audio_ratechange_handler*/ ctx[17]), + listen(audio, "seeking", /*audio_seeking_seeked_handler*/ ctx[18]), + listen(audio, "seeked", /*audio_seeking_seeked_handler*/ ctx[18]), + listen(audio, "ended", /*audio_ended_handler*/ ctx[19]) + ]; + + mounted = true; + } }, p(ctx, [dirty]) { if (!audio_updating && dirty & /*currentTime*/ 8 && !isNaN(/*currentTime*/ ctx[3])) { @@ -98,6 +101,7 @@ function create_fragment(ctx) { o: noop, d(detaching) { if (detaching) detach(audio); + mounted = false; run_all(dispose); } }; diff --git a/test/js/samples/video-bindings/expected.js b/test/js/samples/video-bindings/expected.js index 9c5467e1a0..d3920ef8c2 100644 --- a/test/js/samples/video-bindings/expected.js +++ b/test/js/samples/video-bindings/expected.js @@ -19,6 +19,7 @@ function create_fragment(ctx) { let video_updating = false; let video_animationframe; let video_resize_listener; + let mounted; let dispose; function video_timeupdate_handler() { @@ -38,15 +39,18 @@ function create_fragment(ctx) { if (/*videoHeight*/ ctx[1] === void 0 || /*videoWidth*/ ctx[2] === void 0) add_render_callback(() => /*video_resize_handler*/ ctx[5].call(video)); add_render_callback(() => /*video_elementresize_handler*/ ctx[6].call(video)); }, - m(target, anchor, remount) { + m(target, anchor) { insert(target, video, anchor); video_resize_listener = add_resize_listener(video, /*video_elementresize_handler*/ ctx[6].bind(video)); - if (remount) run_all(dispose); - dispose = [ - listen(video, "timeupdate", video_timeupdate_handler), - listen(video, "resize", /*video_resize_handler*/ ctx[5]) - ]; + if (!mounted) { + dispose = [ + listen(video, "timeupdate", video_timeupdate_handler), + listen(video, "resize", /*video_resize_handler*/ ctx[5]) + ]; + + mounted = true; + } }, p(ctx, [dirty]) { if (!video_updating && dirty & /*currentTime*/ 1 && !isNaN(/*currentTime*/ ctx[0])) { @@ -60,6 +64,7 @@ function create_fragment(ctx) { d(detaching) { if (detaching) detach(video); video_resize_listener(); + mounted = false; run_all(dispose); } }; diff --git a/test/js/samples/window-binding-online/expected.js b/test/js/samples/window-binding-online/expected.js index fa955e4fd5..887195bce1 100644 --- a/test/js/samples/window-binding-online/expected.js +++ b/test/js/samples/window-binding-online/expected.js @@ -10,23 +10,27 @@ import { } from "svelte/internal"; function create_fragment(ctx) { + let mounted; let dispose; add_render_callback(/*onlinestatuschanged*/ ctx[1]); return { c: noop, - m(target, anchor, remount) { - if (remount) run_all(dispose); - - dispose = [ - listen(window, "online", /*onlinestatuschanged*/ ctx[1]), - listen(window, "offline", /*onlinestatuschanged*/ ctx[1]) - ]; + m(target, anchor) { + if (!mounted) { + dispose = [ + listen(window, "online", /*onlinestatuschanged*/ ctx[1]), + listen(window, "offline", /*onlinestatuschanged*/ ctx[1]) + ]; + + mounted = true; + } }, p: noop, i: noop, o: noop, d(detaching) { + mounted = false; run_all(dispose); } }; diff --git a/test/js/samples/window-binding-scroll/expected.js b/test/js/samples/window-binding-scroll/expected.js index 30723cc142..45d992c721 100644 --- a/test/js/samples/window-binding-scroll/expected.js +++ b/test/js/samples/window-binding-scroll/expected.js @@ -25,6 +25,7 @@ function create_fragment(ctx) { let p; let t0; let t1; + let mounted; let dispose; add_render_callback(/*onwindowscroll*/ ctx[1]); @@ -34,18 +35,21 @@ function create_fragment(ctx) { t0 = text("scrolled to "); t1 = text(/*y*/ ctx[0]); }, - m(target, anchor, remount) { + m(target, anchor) { insert(target, p, anchor); append(p, t0); append(p, t1); - if (remount) dispose(); - dispose = listen(window, "scroll", () => { - scrolling = true; - clearTimeout(scrolling_timeout); - scrolling_timeout = setTimeout(clear_scrolling, 100); - /*onwindowscroll*/ ctx[1](); - }); + if (!mounted) { + dispose = listen(window, "scroll", () => { + scrolling = true; + clearTimeout(scrolling_timeout); + scrolling_timeout = setTimeout(clear_scrolling, 100); + /*onwindowscroll*/ ctx[1](); + }); + + mounted = true; + } }, p(ctx, [dirty]) { if (dirty & /*y*/ 1 && !scrolling) { @@ -61,6 +65,7 @@ function create_fragment(ctx) { o: noop, d(detaching) { if (detaching) detach(p); + mounted = false; dispose(); } }; diff --git a/test/runtime/samples/each-block-keyed-component-action/Component.svelte b/test/runtime/samples/each-block-keyed-component-action/Component.svelte new file mode 100644 index 0000000000..18a6c7452a --- /dev/null +++ b/test/runtime/samples/each-block-keyed-component-action/Component.svelte @@ -0,0 +1,5 @@ + + +
diff --git a/test/runtime/samples/each-block-keyed-component-action/_config.js b/test/runtime/samples/each-block-keyed-component-action/_config.js new file mode 100644 index 0000000000..c4421f89ae --- /dev/null +++ b/test/runtime/samples/each-block-keyed-component-action/_config.js @@ -0,0 +1,21 @@ +export default { + test({ assert, component, raf }) { + assert.equal(component.count, 0); + + component.arr = ["2"]; + + assert.equal(component.count, 1); + + component.arr = ["1", "2"]; + + assert.equal(component.count, 2); + + component.arr = ["2", "1"]; + + assert.equal(component.count, 2); + + component.arr = []; + + assert.equal(component.count, 0); + }, +}; diff --git a/test/runtime/samples/each-block-keyed-component-action/main.svelte b/test/runtime/samples/each-block-keyed-component-action/main.svelte new file mode 100644 index 0000000000..bfacdf402a --- /dev/null +++ b/test/runtime/samples/each-block-keyed-component-action/main.svelte @@ -0,0 +1,17 @@ + + +{#each arr as item (item)} + +{/each}