Skip to content

Commit cfd6446

Browse files
committed
fix: exclude derived writes from effect abort and rescheduling
1 parent 9412c58 commit cfd6446

File tree

6 files changed

+48
-1
lines changed

6 files changed

+48
-1
lines changed

.changeset/thick-mice-kick.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'svelte': patch
3+
---
4+
5+
fix: exclude derived writes from effect abort and rescheduling

packages/svelte/src/internal/client/reactivity/batch.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import { invoke_error_boundary } from '../error-handling.js';
3131
import { old_values } from './sources.js';
3232
import { unlink_effect } from './effects.js';
3333
import { unset_context } from './async.js';
34+
import { get_derived_writes, reset_derived_writes } from './deriveds.js';
3435

3536
/** @type {Set<Batch>} */
3637
const batches = new Set();
@@ -546,6 +547,12 @@ function flush_queued_effects(effects) {
546547
if (is_dirty(effect)) {
547548
var wv = write_version;
548549

550+
// updating a derived for also increase the write version but that doesn't mean
551+
// state was written to in the user effect...so we reset the derived writes
552+
// before running the effect so that we can subtract the amount of derived writes
553+
// from the write version when we detect if state was written to in the user effect
554+
reset_derived_writes();
555+
549556
update_effect(effect);
550557

551558
// Effects with no dependencies or teardown do not get added to the effect tree.
@@ -567,7 +574,7 @@ function flush_queued_effects(effects) {
567574

568575
// if state is written in a user effect, abort and re-schedule, lest we run
569576
// effects that should be removed as a result of the state change
570-
if (write_version > wv && (effect.f & USER_EFFECT) !== 0) {
577+
if (write_version - get_derived_writes() > wv && (effect.f & USER_EFFECT) !== 0) {
571578
break;
572579
}
573580
}

packages/svelte/src/internal/client/reactivity/deriveds.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,20 @@ export function execute_derived(derived) {
323323
return value;
324324
}
325325

326+
// in process_effects if state is written to in a user effect we reschedule the rest of the
327+
// tree. However if a derived is updated in an effect we also increase the write version
328+
// so we need to keep track of how many deriveds were written to in the effect so
329+
// that we can subtract that from the write version before rescheduling unnnecessarily
330+
let derived_writes = 0;
331+
332+
export function get_derived_writes() {
333+
return derived_writes;
334+
}
335+
336+
export function reset_derived_writes() {
337+
derived_writes = 0;
338+
}
339+
326340
/**
327341
* @param {Derived} derived
328342
* @returns {void}
@@ -333,6 +347,7 @@ export function update_derived(derived) {
333347
if (!derived.equals(value)) {
334348
derived.v = value;
335349
derived.wv = increment_write_version();
350+
derived_writes++;
336351
}
337352

338353
// don't mark derived clean if we're reading it inside a
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<script>
2+
const der = $derived(false);
3+
4+
$effect(() => {
5+
der
6+
});
7+
</script>
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { test } from '../../test';
2+
3+
export default test({
4+
async test() {}
5+
});
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<script>
2+
import Component from './Component.svelte'
3+
const arr = Array.from({length: 10001});
4+
</script>
5+
6+
{#each arr}
7+
<Component />
8+
{/each}

0 commit comments

Comments
 (0)