fix: don't eagerly execute deriveds on resume (#16150)

* Add failing test

* Add {@const} test case

* Fix the bug

* Add yet another test case

* Better fix

* Changeset

* simplify

* this appears to be unnecessary

---------

Co-authored-by: Rich Harris <rich.harris@vercel.com>
pull/16157/head
Matei Trandafir 3 months ago committed by GitHub
parent 113a3daab2
commit 92ea58bee6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,5 @@
---
"svelte": patch
---
fix: don't eagerly execute deriveds on resume

@ -329,6 +329,7 @@ export function render_effect(fn) {
/**
* @param {(...expressions: any) => void | (() => void)} fn
* @param {Array<() => any>} thunks
* @param {<T>(fn: () => T) => Derived<T>} d
* @returns {Effect}
*/
export function template_effect(fn, thunks = [], d = derived) {
@ -598,15 +599,11 @@ function resume_children(effect, local) {
if ((effect.f & INERT) === 0) return;
effect.f ^= INERT;
// Ensure the effect is marked as clean again so that any dirty child
// effects can schedule themselves for execution
if ((effect.f & CLEAN) === 0) {
effect.f ^= CLEAN;
}
// If a dependency of this effect changed while it was paused,
// schedule the effect to update
if (check_dirtiness(effect)) {
// schedule the effect to update. we don't use `check_dirtiness`
// here because we don't want to eagerly recompute a derived like
// `{#if foo}{foo.bar()}{/if}` if `foo` is now `undefined
if ((effect.f & CLEAN) !== 0) {
set_signal_status(effect, DIRTY);
schedule_effect(effect);
}

@ -0,0 +1,7 @@
<script>
let { value } = $props()
const text = $derived(value.toString())
$effect(() => console.log(text))
</script>

@ -0,0 +1,17 @@
import { flushSync } from 'svelte';
import { test } from '../../test';
export default test({
async test({ assert, target, logs }) {
const [btn1, btn2] = target.querySelectorAll('button');
const [div] = target.querySelectorAll('div');
flushSync(() => btn1?.click());
assert.htmlEqual(div.innerHTML, '123 123');
assert.equal(div.inert, true);
flushSync(() => btn2?.click());
assert.htmlEqual(div.innerHTML, '');
assert.deepEqual(logs, ['123']);
}
});

@ -0,0 +1,23 @@
<script>
import Component from './Component.svelte';
let outer = $state(true);
let inner = $state(123);
function outro() {
return { duration: 100 };
}
</script>
{#if outer}
<div out:outro>
{#if inner}
{@const text = inner.toString()}
{text} {inner.toString()}
<Component value={inner} />
{/if}
</div>
{/if}
<button onclick={() => { outer = false; inner = undefined; }}>Set both to falsy</button>
<button onclick={() => { outer = true }}>Set outer to truthy</button>
Loading…
Cancel
Save