split destroy_signal into destroy_effect and destroy_derived

pull/10760/head
Rich Harris 3 years ago
parent 81a4d87c1f
commit 6e20af4421

@ -1,6 +1,5 @@
import { createClassComponent } from '../../legacy/legacy-client.js';
import { destroy_signal } from './runtime.js';
import { render_effect } from './reactivity/effects.js';
import { destroy_effect, render_effect } from './reactivity/effects.js';
import { open, close } from './render.js';
import { define_property } from './utils.js';
@ -199,7 +198,7 @@ if (typeof HTMLElement === 'function') {
Promise.resolve().then(() => {
if (!this.$$cn) {
this.$$c.$destroy();
destroy_signal(this.$$me);
destroy_effect(this.$$me);
this.$$c = undefined;
}
});

@ -1,14 +1,8 @@
import { is_promise } from '../../../common.js';
import { hydrate_block_anchor } from '../../hydration.js';
import { remove } from '../../reconciler.js';
import {
current_block,
destroy_signal,
execute_effect,
flushSync,
push_destroy_fn
} from '../../runtime.js';
import { render_effect } from '../../reactivity/effects.js';
import { current_block, execute_effect, flushSync, push_destroy_fn } from '../../runtime.js';
import { destroy_effect, render_effect } from '../../reactivity/effects.js';
import { trigger_transitions } from '../../transitions.js';
import { AWAIT_BLOCK, UNINITIALIZED } from '../../constants.js';
@ -74,7 +68,7 @@ export function await_block(anchor_node, input, pending_fn, then_fn, catch_fn) {
remove(render.d);
render.d = null;
}
destroy_signal(render.e);
destroy_effect(render.e);
render.e = null;
}
}
@ -191,7 +185,7 @@ export function await_block(anchor_node, input, pending_fn, then_fn, catch_fn) {
}
const effect = render.e;
if (effect !== null) {
destroy_signal(effect);
destroy_effect(effect);
}
render = render.p;
}

@ -16,8 +16,8 @@ import {
} from '../../hydration.js';
import { clear_text_content, empty, map_get, map_set } from '../../operations.js';
import { insert, remove } from '../../reconciler.js';
import { current_block, destroy_signal, execute_effect, push_destroy_fn } from '../../runtime.js';
import { render_effect } from '../../reactivity/effects.js';
import { current_block, execute_effect, push_destroy_fn } from '../../runtime.js';
import { destroy_effect, render_effect } from '../../reactivity/effects.js';
import { source, mutable_source, set } from '../../reactivity/sources.js';
import { trigger_transitions } from '../../transitions.js';
import { is_array, is_frozen } from '../../utils.js';
@ -133,7 +133,7 @@ function each(anchor_node, collection, flags, key_fn, render_fn, fallback_fn, re
remove(fallback.d);
fallback.d = null;
}
destroy_signal(fallback.e);
destroy_effect(fallback.e);
fallback.e = null;
}
}
@ -273,13 +273,13 @@ function each(anchor_node, collection, flags, key_fn, render_fn, fallback_fn, re
}
const effect = fallback.e;
if (effect !== null) {
destroy_signal(effect);
destroy_effect(effect);
}
fallback = fallback.p;
}
// Clear the array
reconcile_fn([], block, anchor_node, is_controlled, render_fn, flags, false, keys);
destroy_signal(/** @type {import('../../types.js').Effect} */ (render));
destroy_effect(/** @type {import('#client').Effect} */ (render));
});
block.e = each;
@ -904,7 +904,7 @@ export function destroy_each_item_block(
if (!controlled && dom !== null) {
remove(dom);
}
destroy_signal(/** @type {import('../../types.js').Effect} */ (block.e));
destroy_effect(/** @type {import('#client').Effect} */ (block.e));
}
/**

@ -6,8 +6,8 @@ import {
set_current_hydration_fragment
} from '../../hydration.js';
import { remove } from '../../reconciler.js';
import { current_block, destroy_signal, execute_effect, push_destroy_fn } from '../../runtime.js';
import { render_effect } from '../../reactivity/effects.js';
import { current_block, execute_effect, push_destroy_fn } from '../../runtime.js';
import { destroy_effect, render_effect } from '../../reactivity/effects.js';
import { trigger_transitions } from '../../transitions.js';
/** @returns {import('../../types.js').IfBlock} */
@ -174,8 +174,8 @@ export function if_block(anchor_node, condition_fn, consequent_fn, alternate_fn)
if (alternate_dom !== null) {
remove(alternate_dom);
}
destroy_signal(consequent_effect);
destroy_signal(alternate_effect);
destroy_effect(consequent_effect);
destroy_effect(alternate_effect);
});
block.e = if_effect;
}

@ -1,8 +1,8 @@
import { UNINITIALIZED, KEY_BLOCK } from '../../constants.js';
import { hydrate_block_anchor } from '../../hydration.js';
import { remove } from '../../reconciler.js';
import { current_block, destroy_signal, execute_effect, push_destroy_fn } from '../../runtime.js';
import { render_effect } from '../../reactivity/effects.js';
import { current_block, execute_effect, push_destroy_fn } from '../../runtime.js';
import { destroy_effect, render_effect } from '../../reactivity/effects.js';
import { trigger_transitions } from '../../transitions.js';
import { safe_not_equal } from '../../reactivity/equality.js';
@ -58,7 +58,7 @@ export function key_block(anchor_node, key, render_fn) {
remove(render.d);
render.d = null;
}
destroy_signal(render.e);
destroy_effect(render.e);
render.e = null;
}
}
@ -131,7 +131,7 @@ export function key_block(anchor_node, key, render_fn) {
}
const effect = render.e;
if (effect !== null) {
destroy_signal(effect);
destroy_effect(effect);
}
render = render.p;
}

@ -1,8 +1,18 @@
import { DEV } from 'esm-env';
import { CLEAN, DERIVED, UNINITIALIZED, UNOWNED } from '../constants.js';
import { current_block, current_consumer, current_effect } from '../runtime.js';
import { CLEAN, DERIVED, DESTROYED, UNINITIALIZED, UNOWNED } from '../constants.js';
import {
IS_EFFECT,
current_block,
current_consumer,
current_effect,
destroy_references,
remove_consumers,
set_signal_status
} from '../runtime.js';
import { push_reference } from './effects.js';
import { default_equals, safe_equal } from './equality.js';
import { is_array } from '../utils.js';
import { run_all } from '../../common.js';
/**
* @template V
@ -52,3 +62,27 @@ export function derived_safe_equal(fn) {
signal.e = safe_equal;
return signal;
}
/**
* @param {import('./types.js').Derived} signal
* @returns {void}
*/
export function destroy_derived(signal) {
const teardown = /** @type {null | (() => void)} */ (signal.v);
const destroy = signal.y;
const flags = signal.f;
destroy_references(signal);
remove_consumers(signal, 0);
signal.i = signal.r = signal.y = signal.x = signal.b = signal.d = signal.c = null;
set_signal_status(signal, DESTROYED);
if (destroy !== null) {
if (is_array(destroy)) {
run_all(destroy);
} else {
destroy();
}
}
if (teardown !== null && (flags & IS_EFFECT) !== 0) {
teardown();
}
}

@ -1,17 +1,22 @@
import { DEV } from 'esm-env';
import {
IS_EFFECT,
current_block,
current_component_context,
current_effect,
destroy_signal,
destroy_references,
flush_local_render_effects,
get,
is_runes,
remove_consumers,
schedule_effect,
set_signal_status,
untrack
} from '../runtime.js';
import { DIRTY, MANAGED, RENDER_EFFECT, EFFECT, PRE_EFFECT } from '../constants.js';
import { DIRTY, MANAGED, RENDER_EFFECT, EFFECT, PRE_EFFECT, DESTROYED } from '../constants.js';
import { set } from './sources.js';
import { is_array } from '../utils.js';
import { run_all } from '../../common.js';
/**
* @param {import('#client').Reaction} target_signal
@ -116,7 +121,7 @@ export function user_effect(fn) {
export function user_root_effect(fn) {
const effect = render_effect(fn, current_block, true);
return () => {
destroy_signal(effect);
destroy_effect(effect);
};
}
@ -241,3 +246,27 @@ export function render_effect(fn, block = current_block, managed = false, sync =
}
return create_effect(flags, /** @type {any} */ (fn), sync, block, true);
}
/**
* @param {import('./types.js').Effect} signal
* @returns {void}
*/
export function destroy_effect(signal) {
const teardown = /** @type {null | (() => void)} */ (signal.v);
const destroy = signal.y;
const flags = signal.f;
destroy_references(signal);
remove_consumers(signal, 0);
signal.i = signal.r = signal.y = signal.x = signal.b = signal.d = signal.c = null;
set_signal_status(signal, DESTROYED);
if (destroy !== null) {
if (is_array(destroy)) {
run_all(destroy);
} else {
destroy();
}
}
if (teardown !== null && (flags & IS_EFFECT) !== 0) {
teardown();
}
}

@ -35,7 +35,6 @@ import {
remove
} from './reconciler.js';
import {
destroy_signal,
push_destroy_fn,
execute_effect,
untrack,
@ -55,7 +54,8 @@ import {
effect,
managed_effect,
pre_effect,
user_effect
user_effect,
destroy_effect
} from './reactivity/effects.js';
import {
current_hydration_fragment,
@ -728,11 +728,11 @@ export function bind_playback_rate(media, get_value, update) {
// Needs to happen after the element is inserted into the dom, else playback will be set back to 1 by the browser.
// For hydration we could do it immediately but the additional code is not worth the lost microtask.
/** @type {import('./types.js').Reaction | undefined} */
/** @type {import('./types.js').Effect | undefined} */
let render;
let destroyed = false;
const effect = managed_effect(() => {
destroy_signal(effect);
destroy_effect(effect);
if (destroyed) return;
if (get_value() == null) {
callback();
@ -750,7 +750,7 @@ export function bind_playback_rate(media, get_value, update) {
render_effect(() => () => {
destroyed = true;
if (render) {
destroy_signal(render);
destroy_effect(render);
}
});
}
@ -1672,7 +1672,7 @@ export function element(anchor_node, tag_fn, is_svg, render_fn) {
block.d = null;
element = null;
}
destroy_signal(render_effect_signal);
destroy_effect(render_effect_signal);
});
block.e = element_effect;
}
@ -1712,7 +1712,7 @@ export function component(anchor_node, component_fn, render_fn) {
remove(render.d);
render.d = null;
}
destroy_signal(render.e);
destroy_effect(render.e);
render.e = null;
}
}
@ -1788,7 +1788,7 @@ export function component(anchor_node, component_fn, render_fn) {
}
const effect = render.e;
if (effect !== null) {
destroy_signal(effect);
destroy_effect(effect);
}
render = render.p;
}
@ -2659,7 +2659,7 @@ function _mount(Component, options) {
if (dom !== null) {
remove(dom);
}
destroy_signal(/** @type {import('./types.js').Effect} */ (block.e));
destroy_effect(/** @type {import('./types.js').Effect} */ (block.e));
});
return component;

@ -10,7 +10,7 @@ import {
object_prototype
} from './utils.js';
import { unstate } from './proxy.js';
import { pre_effect } from './reactivity/effects.js';
import { destroy_effect, pre_effect } from './reactivity/effects.js';
import {
EACH_BLOCK,
IF_BLOCK,
@ -31,8 +31,9 @@ import {
import { flush_tasks } from './dom/task.js';
import { add_owner } from './dev/ownership.js';
import { mutate, set, source } from './reactivity/sources.js';
import { destroy_derived } from './reactivity/deriveds.js';
const IS_EFFECT = EFFECT | PRE_EFFECT | RENDER_EFFECT;
export const IS_EFFECT = EFFECT | PRE_EFFECT | RENDER_EFFECT;
const FLUSH_MICROTASK = 0;
const FLUSH_SYNC = 1;
@ -354,7 +355,7 @@ function remove_consumer(signal, dependency) {
* @param {number} start_index
* @returns {void}
*/
function remove_consumers(signal, start_index) {
export function remove_consumers(signal, start_index) {
const dependencies = signal.d;
if (dependencies !== null) {
const active_dependencies = start_index === 0 ? null : dependencies.slice(0, start_index);
@ -373,13 +374,19 @@ function remove_consumers(signal, start_index) {
* @param {import('./types.js').Reaction} signal
* @returns {void}
*/
function destroy_references(signal) {
export function destroy_references(signal) {
const references = signal.r;
signal.r = null;
if (references !== null) {
let i;
for (i = 0; i < references.length; i++) {
destroy_signal(references[i]);
var reference = references[i];
if ((reference.f & DERIVED) !== 0) {
// TODO make signal.r only contain deriveds or effects
destroy_derived(/** @type {import('#client').Derived} */ (reference));
} else {
destroy_effect(/** @type {import('#client').Effect} */ (reference));
}
}
}
}
@ -898,30 +905,6 @@ export function mark_signal_consumers(signal, to_status, force_schedule) {
}
}
/**
* @param {import('./types.js').Reaction} signal
* @returns {void}
*/
export function destroy_signal(signal) {
const teardown = /** @type {null | (() => void)} */ (signal.v);
const destroy = signal.y;
const flags = signal.f;
destroy_references(signal);
remove_consumers(signal, 0);
signal.i = signal.r = signal.y = signal.x = signal.b = signal.d = signal.c = null;
set_signal_status(signal, DESTROYED);
if (destroy !== null) {
if (is_array(destroy)) {
run_all(destroy);
} else {
destroy();
}
}
if (teardown !== null && (flags & IS_EFFECT) !== 0) {
teardown();
}
}
/**
* Use `untrack` to prevent something from being treated as an `$effect`/`$derived` dependency.
*

@ -12,11 +12,15 @@ import {
import { destroy_each_item_block, get_first_element } from './dom/blocks/each.js';
import { schedule_raf_task } from './dom/task.js';
import { append_child, empty } from './operations.js';
import { effect, managed_effect, managed_pre_effect } from './reactivity/effects.js';
import {
destroy_effect,
effect,
managed_effect,
managed_pre_effect
} from './reactivity/effects.js';
import {
current_block,
current_effect,
destroy_signal,
execute_effect,
mark_subtree_inert,
untrack
@ -589,7 +593,7 @@ export function bind_transition(dom, get_transition_fn, props_fn, direction, glo
}
const effect = managed_pre_effect(() => {
destroy_signal(effect);
destroy_effect(effect);
dom.inert = false;
if (show_intro && !already_mounted) {
@ -666,9 +670,9 @@ export function trigger_transitions(transitions, target_direction, from) {
if (outros.length > 0) {
// Defer the outros to a microtask
const e = managed_pre_effect(() => {
destroy_signal(e);
destroy_effect(e);
const e2 = managed_effect(() => {
destroy_signal(e2);
destroy_effect(e2);
run_all(outros);
});
}, false);

@ -1,6 +1,11 @@
import { describe, assert, it } from 'vitest';
import * as $ from '../../src/internal/client/runtime';
import { effect, render_effect, user_effect } from '../../src/internal/client/reactivity/effects';
import {
destroy_effect,
effect,
render_effect,
user_effect
} from '../../src/internal/client/reactivity/effects';
import { source, set } from '../../src/internal/client/reactivity/sources';
import type { Derived } from '../../src/internal/client/types';
import { proxy } from '../../src/internal/client/proxy';
@ -27,7 +32,7 @@ function run_test(runes: boolean, fn: (runes: boolean) => () => void) {
);
$.pop();
execute();
$.destroy_signal(signal);
destroy_effect(signal);
};
}
@ -261,7 +266,7 @@ describe('signals', () => {
// Ensure we're not leaking consumers
assert.deepEqual(count.c?.length, 1);
assert.deepEqual(log, [0, 2, 'limit', 0]);
$.destroy_signal(effect);
destroy_effect(effect);
// Ensure we're not leaking consumers
assert.deepEqual(count.c, null);
};

Loading…
Cancel
Save