fix: prevent transition action overfiring (#10163)

pull/10171/head
Dominic Gannaway 9 months ago committed by GitHub
parent f11f451bdb
commit a26012fc62
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -0,0 +1,5 @@
---
'svelte': patch
---
fix: prevent transition action overfiring

@ -1375,7 +1375,10 @@ function if_block(anchor_node, condition_fn, consequent_fn, alternate_fn) {
/** @type {null | import('./types.js').TemplateNode | Array<import('./types.js').TemplateNode>} */
let alternate_dom = null;
let has_mounted = false;
let has_mounted_branch = false;
/**
* @type {import("./types.js").EffectSignal | null}
*/
let current_branch_effect = null;
const if_effect = render_effect(
() => {
@ -1432,20 +1435,24 @@ function if_block(anchor_node, condition_fn, consequent_fn, alternate_fn) {
);
// Managed effect
const consequent_effect = render_effect(
() => {
if (consequent_dom !== null) {
(
/** @type {any} */ _,
/** @type {import("./types.js").EffectSignal | null} */ consequent_effect
) => {
const result = block.v;
if (!result && consequent_dom !== null) {
remove(consequent_dom);
consequent_dom = null;
}
if (block.v) {
if (result && current_branch_effect !== consequent_effect) {
consequent_fn(anchor_node);
if (!has_mounted_branch) {
if (current_branch_effect === null) {
// Restore previous fragment so that Svelte continues to operate in hydration mode
set_current_hydration_fragment(previous_hydration_fragment);
has_mounted_branch = true;
}
current_branch_effect = consequent_effect;
consequent_dom = block.d;
}
consequent_dom = block.d;
block.d = null;
},
block,
@ -1454,22 +1461,26 @@ function if_block(anchor_node, condition_fn, consequent_fn, alternate_fn) {
block.ce = consequent_effect;
// Managed effect
const alternate_effect = render_effect(
() => {
if (alternate_dom !== null) {
(
/** @type {any} */ _,
/** @type {import("./types.js").EffectSignal | null} */ alternate_effect
) => {
const result = block.v;
if (result && alternate_dom !== null) {
remove(alternate_dom);
alternate_dom = null;
}
if (!block.v) {
if (!result && current_branch_effect !== alternate_effect) {
if (alternate_fn !== null) {
alternate_fn(anchor_node);
}
if (!has_mounted_branch) {
if (current_branch_effect === null) {
// Restore previous fragment so that Svelte continues to operate in hydration mode
set_current_hydration_fragment(previous_hydration_fragment);
has_mounted_branch = true;
}
current_branch_effect = alternate_effect;
alternate_dom = block.d;
}
alternate_dom = block.d;
block.d = null;
},
block,

@ -342,9 +342,13 @@ function execute_signal_fn(signal) {
try {
let res;
if (is_render_effect) {
res = /** @type {(block: import('./types.js').Block) => V} */ (init)(
/** @type {import('./types.js').Block} */ (signal.b)
);
res =
/** @type {(block: import('./types.js').Block, signal: import('./types.js').Signal) => V} */ (
init
)(
/** @type {import('./types.js').Block} */ (signal.b),
/** @type {import('./types.js').Signal} */ (signal)
);
} else {
res = /** @type {() => V} */ (init)();
}

@ -31,7 +31,6 @@ const DELAY_NEXT_TICK = Number.MIN_SAFE_INTEGER;
/** @type {undefined | number} */
let active_tick_ref = undefined;
let skip_mount_intro = false;
/**
* @template T
@ -496,7 +495,7 @@ export function bind_transition(dom, get_transition_fn, props_fn, direction, glo
can_show_intro_on_mount = true;
} else if (transition_block.t === IF_BLOCK) {
transition_block.r = if_block_transition;
if (can_show_intro_on_mount && !skip_mount_intro) {
if (can_show_intro_on_mount) {
/** @type {import('./types.js').Block | null} */
let if_block = transition_block;
while (if_block.t === IF_BLOCK) {
@ -511,14 +510,13 @@ export function bind_transition(dom, get_transition_fn, props_fn, direction, glo
}
}
if (!can_apply_lazy_transitions && can_show_intro_on_mount) {
can_show_intro_on_mount = !skip_mount_intro && transition_block.e !== null;
can_show_intro_on_mount = transition_block.e !== null;
}
if (can_show_intro_on_mount || !global) {
can_apply_lazy_transitions = true;
}
} else if (transition_block.t === ROOT_BLOCK && !can_apply_lazy_transitions) {
can_show_intro_on_mount =
!skip_mount_intro && (transition_block.e !== null || transition_block.i);
can_show_intro_on_mount = transition_block.e !== null || transition_block.i;
}
transition_block = transition_block.p;
}
@ -527,14 +525,11 @@ export function bind_transition(dom, get_transition_fn, props_fn, direction, glo
let transition;
effect(() => {
let already_mounted = false;
if (transition !== undefined) {
already_mounted = true;
// Destroy any existing transitions first
try {
skip_mount_intro = true;
transition.x();
} finally {
skip_mount_intro = false;
}
transition.x();
}
const transition_fn = get_transition_fn();
/** @param {DOMRect} [from] */
@ -557,7 +552,7 @@ export function bind_transition(dom, get_transition_fn, props_fn, direction, glo
const is_intro = direction === 'in';
const show_intro = can_show_intro_on_mount && (is_intro || direction === 'both');
if (show_intro) {
if (show_intro && !already_mounted) {
transition.p = transition.i();
}
@ -565,7 +560,7 @@ export function bind_transition(dom, get_transition_fn, props_fn, direction, glo
destroy_signal(effect);
dom.inert = false;
if (show_intro) {
if (show_intro && !already_mounted) {
transition.in();
}
@ -662,7 +657,8 @@ function if_block_transition(transition) {
const c = /** @type {Set<import('./types.js').Transition>} */ (consequent_transitions);
c.delete(transition);
if (c.size === 0) {
execute_effect(/** @type {import('./types.js').EffectSignal} */ (block.ce));
const consequent_effect = block.ce;
execute_effect(/** @type {import('./types.js').EffectSignal} */ (consequent_effect));
}
});
} else {
@ -675,7 +671,8 @@ function if_block_transition(transition) {
const a = /** @type {Set<import('./types.js').Transition>} */ (alternate_transitions);
a.delete(transition);
if (a.size === 0) {
execute_effect(/** @type {import('./types.js').EffectSignal} */ (block.ae));
const alternate_effect = block.ae;
execute_effect(/** @type {import('./types.js').EffectSignal} */ (alternate_effect));
}
});
}

@ -99,7 +99,11 @@ export type ComputationSignal<V = unknown> = {
/** The types that the signal represent, as a bitwise value */
f: SignalFlags;
/** init: The function that we invoke for effects and computeds */
i: null | (() => V) | (() => void | (() => void)) | ((b: Block) => void | (() => void));
i:
| null
| (() => V)
| (() => void | (() => void))
| ((b: Block, s: Signal) => void | (() => void));
/** references: Anything that a signal owns */
r: null | ComputationSignal[];
/** value: The latest value for this signal, doubles as the teardown for effects */

@ -20,6 +20,12 @@ export default test({
b2.click();
});
assert.deepEqual(log, ['transition 2']);
flushSync(() => {
b1.click();
});
assert.deepEqual(log, ['transition 2', 'transition 1']);
}
});

Loading…
Cancel
Save