diff --git a/.changeset/lemon-meals-appear.md b/.changeset/lemon-meals-appear.md new file mode 100644 index 0000000000..c8c7b4e6a0 --- /dev/null +++ b/.changeset/lemon-meals-appear.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +chore: optimise effects that only exist to return a teardown diff --git a/packages/svelte/src/internal/client/dom/elements/bindings/input.js b/packages/svelte/src/internal/client/dom/elements/bindings/input.js index 1196a60339..85296fd3f4 100644 --- a/packages/svelte/src/internal/client/dom/elements/bindings/input.js +++ b/packages/svelte/src/internal/client/dom/elements/bindings/input.js @@ -1,5 +1,5 @@ import { DEV } from 'esm-env'; -import { render_effect, effect } from '../../../reactivity/effects.js'; +import { render_effect, effect, teardown } from '../../../reactivity/effects.js'; import { stringify } from '../../../render.js'; import { listen_to_event_and_reset_event } from './shared.js'; import * as e from '../../../errors.js'; @@ -103,14 +103,12 @@ export function bind_group(inputs, group_index, input, get_value, update) { } }); - render_effect(() => { - return () => { - var index = binding_group.indexOf(input); + teardown(() => { + var index = binding_group.indexOf(input); - if (index !== -1) { - binding_group.splice(index, 1); - } - }; + if (index !== -1) { + binding_group.splice(index, 1); + } }); effect(() => { @@ -189,6 +187,7 @@ export function bind_files(input, get_value, update) { listen_to_event_and_reset_event(input, 'change', () => { update(input.files); }); + render_effect(() => { input.files = get_value(); }); diff --git a/packages/svelte/src/internal/client/dom/elements/bindings/media.js b/packages/svelte/src/internal/client/dom/elements/bindings/media.js index 56606168c8..b7708b4f34 100644 --- a/packages/svelte/src/internal/client/dom/elements/bindings/media.js +++ b/packages/svelte/src/internal/client/dom/elements/bindings/media.js @@ -1,5 +1,5 @@ import { hydrating } from '../../hydration.js'; -import { render_effect, effect } from '../../../reactivity/effects.js'; +import { render_effect, effect, teardown } from '../../../reactivity/effects.js'; import { listen } from './shared.js'; /** @param {TimeRanges} ranges */ @@ -52,7 +52,7 @@ export function bind_current_time(media, get_value, update) { updating = false; }); - render_effect(() => () => cancelAnimationFrame(raf_id)); + teardown(() => cancelAnimationFrame(raf_id)); } /** diff --git a/packages/svelte/src/internal/client/dom/elements/bindings/props.js b/packages/svelte/src/internal/client/dom/elements/bindings/props.js index 4b5a24cc92..5337d34e2d 100644 --- a/packages/svelte/src/internal/client/dom/elements/bindings/props.js +++ b/packages/svelte/src/internal/client/dom/elements/bindings/props.js @@ -1,4 +1,4 @@ -import { render_effect } from '../../../reactivity/effects.js'; +import { teardown } from '../../../reactivity/effects.js'; import { get_descriptor } from '../../../utils.js'; /** @@ -15,7 +15,7 @@ export function bind_prop(props, prop, value) { if (desc && desc.set) { props[prop] = value; - render_effect(() => () => { + teardown(() => { props[prop] = null; }); } diff --git a/packages/svelte/src/internal/client/dom/elements/bindings/shared.js b/packages/svelte/src/internal/client/dom/elements/bindings/shared.js index fd8d6ae7f4..195eae2200 100644 --- a/packages/svelte/src/internal/client/dom/elements/bindings/shared.js +++ b/packages/svelte/src/internal/client/dom/elements/bindings/shared.js @@ -1,4 +1,4 @@ -import { render_effect } from '../../../reactivity/effects.js'; +import { teardown } from '../../../reactivity/effects.js'; import { add_form_reset_listener } from '../misc.js'; /** @@ -18,12 +18,10 @@ export function listen(target, events, handler, call_handler_immediately = true) target.addEventListener(name, handler); } - render_effect(() => { - return () => { - for (var name of events) { - target.removeEventListener(name, handler); - } - }; + teardown(() => { + for (var name of events) { + target.removeEventListener(name, handler); + } }); } diff --git a/packages/svelte/src/internal/client/dom/elements/bindings/size.js b/packages/svelte/src/internal/client/dom/elements/bindings/size.js index 5dcd211b71..e0ad045d80 100644 --- a/packages/svelte/src/internal/client/dom/elements/bindings/size.js +++ b/packages/svelte/src/internal/client/dom/elements/bindings/size.js @@ -1,5 +1,4 @@ -import { effect, render_effect } from '../../../reactivity/effects.js'; -import { untrack } from '../../../runtime.js'; +import { effect, teardown } from '../../../reactivity/effects.js'; /** * Resize observer singleton. @@ -89,7 +88,7 @@ export function bind_resize_observer(element, type, update) { : resize_observer_device_pixel_content_box; var unsub = observer.observe(element, /** @param {any} entry */ (entry) => update(entry[type])); - render_effect(() => unsub); + teardown(unsub); } /** diff --git a/packages/svelte/src/internal/client/dom/elements/bindings/universal.js b/packages/svelte/src/internal/client/dom/elements/bindings/universal.js index 7e8b3f3bf4..0e825a5800 100644 --- a/packages/svelte/src/internal/client/dom/elements/bindings/universal.js +++ b/packages/svelte/src/internal/client/dom/elements/bindings/universal.js @@ -1,4 +1,4 @@ -import { render_effect } from '../../../reactivity/effects.js'; +import { render_effect, teardown } from '../../../reactivity/effects.js'; import { listen } from './shared.js'; /** @@ -57,10 +57,8 @@ export function bind_property(property, event_name, element, set, get) { // @ts-ignore if (element === document.body || element === window || element === document) { - render_effect(() => { - return () => { - element.removeEventListener(event_name, handler); - }; + teardown(() => { + element.removeEventListener(event_name, handler); }); } } diff --git a/packages/svelte/src/internal/client/dom/elements/bindings/window.js b/packages/svelte/src/internal/client/dom/elements/bindings/window.js index 4e01211294..23f594a0fe 100644 --- a/packages/svelte/src/internal/client/dom/elements/bindings/window.js +++ b/packages/svelte/src/internal/client/dom/elements/bindings/window.js @@ -1,4 +1,4 @@ -import { effect, render_effect } from '../../../reactivity/effects.js'; +import { effect, render_effect, teardown } from '../../../reactivity/effects.js'; import { listen } from './shared.js'; /** @@ -51,10 +51,8 @@ export function bind_window_scroll(type, get_value, update) { // Browsers don't fire the scroll event for the initial scroll position when scroll style isn't set to smooth effect(target_handler); - render_effect(() => { - return () => { - removeEventListener('scroll', target_handler); - }; + teardown(() => { + removeEventListener('scroll', target_handler); }); } diff --git a/packages/svelte/src/internal/client/dom/elements/events.js b/packages/svelte/src/internal/client/dom/elements/events.js index 33cb2c3ca0..14aeb65c26 100644 --- a/packages/svelte/src/internal/client/dom/elements/events.js +++ b/packages/svelte/src/internal/client/dom/elements/events.js @@ -1,4 +1,4 @@ -import { render_effect } from '../../reactivity/effects.js'; +import { render_effect, teardown } from '../../reactivity/effects.js'; import { all_registered_events, root_event_handles } from '../../render.js'; import { define_property, is_array } from '../../utils.js'; import { hydrating } from '../hydration.js'; @@ -98,10 +98,8 @@ export function event(event_name, dom, handler, capture, passive) { // @ts-ignore if (dom === document.body || dom === window || dom === document) { - render_effect(() => { - return () => { - dom.removeEventListener(event_name, target_handler, options); - }; + teardown(() => { + dom.removeEventListener(event_name, target_handler, options); }); } } diff --git a/packages/svelte/src/internal/client/reactivity/effects.js b/packages/svelte/src/internal/client/reactivity/effects.js index f7db38f3d1..4ae4ce2870 100644 --- a/packages/svelte/src/internal/client/reactivity/effects.js +++ b/packages/svelte/src/internal/client/reactivity/effects.js @@ -30,7 +30,8 @@ import { ROOT_EFFECT, EFFECT_TRANSPARENT, DERIVED, - UNOWNED + UNOWNED, + CLEAN } from '../constants.js'; import { set } from './sources.js'; import { remove } from '../dom/reconciler.js'; @@ -68,7 +69,7 @@ export function push_effect(effect, parent_effect) { /** * @param {number} type - * @param {(() => void | (() => void))} fn + * @param {null | (() => void | (() => void))} fn * @param {boolean} sync * @returns {import('#client').Effect} */ @@ -121,7 +122,7 @@ function create_effect(type, fn, sync) { } finally { set_is_flushing_effect(previously_flushing_effect); } - } else { + } else if (fn !== null) { schedule_effect(effect); } @@ -144,6 +145,16 @@ export function effect_active() { return false; } +/** + * @param {() => void} fn + */ +export function teardown(fn) { + const effect = create_effect(RENDER_EFFECT, null, false); + set_signal_status(effect, CLEAN); + effect.teardown = fn; + return effect; +} + /** * Internal representation of `$effect(...)` * @param {() => void | (() => void)} fn @@ -364,7 +375,6 @@ export function destroy_effect(effect, remove_dom = true) { effect.dom = effect.deps = effect.parent = - // @ts-expect-error effect.fn = null; } diff --git a/packages/svelte/src/internal/client/reactivity/types.d.ts b/packages/svelte/src/internal/client/reactivity/types.d.ts index 7502443bd1..e1d420c570 100644 --- a/packages/svelte/src/internal/client/reactivity/types.d.ts +++ b/packages/svelte/src/internal/client/reactivity/types.d.ts @@ -18,7 +18,7 @@ export interface Value extends Signal { export interface Reaction extends Signal { /** The reaction function */ - fn: Function; + fn: null | Function; /** Signals that this signal reads from */ deps: null | Value[]; /** First child effect created inside this signal */ @@ -40,7 +40,7 @@ export interface Effect extends Reaction { /** The associated component context */ ctx: null | ComponentContext; /** The effect function */ - fn: () => void | (() => void); + fn: null | (() => void | (() => void)); /** The teardown function returned from the effect function */ teardown: null | (() => void); /** Transition managers created with `$.transition` */ diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index 36d0602275..ce4ebf3ab4 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -290,7 +290,7 @@ function handle_error(error, effect, component_context) { const component_stack = []; - const effect_name = effect.fn.name; + const effect_name = effect.fn?.name; if (effect_name) { component_stack.push(effect_name); @@ -358,7 +358,7 @@ export function execute_reaction_fn(signal) { current_untracking = false; try { - let res = (0, signal.fn)(); + let res = /** @type {Function} */ (0, signal.fn)(); let dependencies = /** @type {import('#client').Value[]} **/ (signal.deps); if (current_dependencies !== null) { let i;