diff --git a/src/runtime/internal/Component.ts b/src/runtime/internal/Component.ts index 3e48010547..f642869f49 100644 --- a/src/runtime/internal/Component.ts +++ b/src/runtime/internal/Component.ts @@ -1,4 +1,4 @@ -import { add_render_callback, flush, schedule_update, dirty_components } from './scheduler'; +import { add_render_callback, flush, flush_render_callbacks, schedule_update, dirty_components } from './scheduler'; import { current_component, set_current_component } from './lifecycle'; import { blank_object, is_empty, is_function, run, run_all, noop } from './utils'; import { children, detach, start_hydrating, end_hydrating } from './dom'; @@ -84,6 +84,8 @@ export function mount_component(component, target, anchor, customElement) { export function destroy_component(component, detaching) { const $$ = component.$$; if ($$.fragment !== null) { + flush_render_callbacks($$.after_update); + run_all($$.on_destroy); $$.fragment && $$.fragment.d(detaching); diff --git a/src/runtime/internal/scheduler.ts b/src/runtime/internal/scheduler.ts index c0b8e57b08..92c5898a61 100644 --- a/src/runtime/internal/scheduler.ts +++ b/src/runtime/internal/scheduler.ts @@ -5,7 +5,7 @@ export const dirty_components = []; export const intros = { enabled: false }; export const binding_callbacks = []; -const render_callbacks = []; +let render_callbacks = []; const flush_callbacks = []; const resolved_promise = Promise.resolve(); @@ -107,3 +107,13 @@ function update($$) { $$.after_update.forEach(add_render_callback); } } + +// For example, execute remaining `afterUpdate` before execute `destroy` through this function. +// Doing this will prevent to execute `afterUpdate` after execute `destroy`. +export function flush_render_callbacks(fns: Function[]) { + const filtered = []; + const targets = []; + render_callbacks.forEach((c) => fns.indexOf(c) === -1 ? filtered.push(c) : targets.push(c)); + targets.forEach((c) => c()); + render_callbacks = filtered; +} diff --git a/test/runtime/samples/action-update-before-destroy/Component.svelte b/test/runtime/samples/action-update-before-destroy/Component.svelte new file mode 100644 index 0000000000..e38a0fff64 --- /dev/null +++ b/test/runtime/samples/action-update-before-destroy/Component.svelte @@ -0,0 +1,27 @@ + + + +{#if selected} +
{item.id}
+{/if} diff --git a/test/runtime/samples/action-update-before-destroy/_config.js b/test/runtime/samples/action-update-before-destroy/_config.js new file mode 100644 index 0000000000..a822c4d8a5 --- /dev/null +++ b/test/runtime/samples/action-update-before-destroy/_config.js @@ -0,0 +1,16 @@ +export default { + html: ` + +
1
+ `, + async test({ assert, target, window }) { + const button = target.querySelector('button'); + const event = new window.MouseEvent('click'); + const messages = []; + const log = console.log; + console.log = msg => messages.push(msg); + await button.dispatchEvent(event); + console.log = log; + assert.deepEqual(messages, ['afterUpdate', 'onDestroy']); + } +}; diff --git a/test/runtime/samples/action-update-before-destroy/main.svelte b/test/runtime/samples/action-update-before-destroy/main.svelte new file mode 100644 index 0000000000..7f99ce0f38 --- /dev/null +++ b/test/runtime/samples/action-update-before-destroy/main.svelte @@ -0,0 +1,10 @@ + + +{#each Object.values($items) as item (item.id)} + +{/each}