fix: skip unnecessary derived effect in earlier batch (#18525)

fixes #18438 
Currently the `mark` method inside `#merge` for `earlier_batch` will
schedule effect for undirty derived. Add the condition for derived to
prevent unnecessary effect be scheduled.

```
if ((flags & DERIVED) !== 0) {
	mark(/** @type {Derived} */ (reaction));
}
```
pull/18527/head
JY Wey 3 days ago committed by GitHub
parent c2b7642263
commit bfbb026f2f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,5 @@
---
'svelte': patch
---
fix: skip unnecessary derived effect in earlier batch

@ -530,6 +530,12 @@ export class Batch {
const mark = (value) => {
var reactions = value.reactions;
if (reactions === null) return;
// skip if value is derived and is neither dirty nor maybe dirty. transitive
// deriveds (a derived depending on another derived) are only MAYBE_DIRTY, so
// we must continue traversing them to reach the effects that depend on them
if ((value.f & DERIVED) !== 0 && (value.f & (DIRTY | MAYBE_DIRTY)) === 0) {
return;
}
for (const reaction of reactions) {
var flags = reaction.f;

@ -0,0 +1,28 @@
import { tick } from 'svelte';
import { test } from '../../test';
export default test({
async test({ assert, target }) {
const [increment, pop] = target.querySelectorAll('button');
increment.click();
await tick();
assert.htmlEqual(
target.innerHTML,
`<button>increment</button> <button>pop</button> <p>Loading...</p>`
);
increment.click();
await tick();
assert.htmlEqual(
target.innerHTML,
`<button>increment</button> <button>pop</button> <p>Loading...</p>`
);
pop.click();
await tick();
assert.htmlEqual(target.innerHTML, `<button>increment</button> <button>pop</button> 2 2 1`);
pop.click();
await tick();
assert.htmlEqual(target.innerHTML, `<button>increment</button> <button>pop</button> 2 2 1`);
}
});

@ -0,0 +1,29 @@
<script>
let count = $state(0);
let other = $state(0);
const queue = [];
let pending = $derived(defaultPending);
function push(v) {
return new Promise((resolve) => queue.push(() => resolve(v)));
}
</script>
<button onclick={() => {
if (count === 0) {
other++;
count++;
} else {
count++
}
}}>increment</button>
<button onclick={() => queue.pop()?.()}>pop</button>
{#snippet defaultPending()}
<p>Loading...</p>
{/snippet}
{#if count > 0}
<svelte:boundary {pending}>
{await push(count)} {count} {other}
</svelte:boundary>
{/if}
Loading…
Cancel
Save