Skip to content

Commit 0134d04

Browse files
committed
preserve DIRTY/MAYBE_DIRTY status of deferred effects
1 parent a826136 commit 0134d04

File tree

3 files changed

+80
-13
lines changed

3 files changed

+80
-13
lines changed

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

Lines changed: 35 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ import {
1010
INERT,
1111
RENDER_EFFECT,
1212
ROOT_EFFECT,
13-
USER_EFFECT
13+
USER_EFFECT,
14+
MAYBE_DIRTY
1415
} from '#client/constants';
1516
import { async_mode_flag } from '../../flags/index.js';
1617
import { deferred, define_property } from '../../shared/utils.js';
@@ -146,6 +147,18 @@ export class Batch {
146147
*/
147148
#block_effects = [];
148149

150+
/**
151+
* Deferred effects (which run after async work has completed) that are DIRTY
152+
* @type {Effect[]}
153+
*/
154+
#dirty_effects = [];
155+
156+
/**
157+
* Deferred effects that are MAYBE_DIRTY
158+
* @type {Effect[]}
159+
*/
160+
#maybe_dirty_effects = [];
161+
149162
/**
150163
* A set of branches that still exist, but will be destroyed when this batch
151164
* is committed — we skip over these during `process`
@@ -221,10 +234,9 @@ export class Batch {
221234

222235
this.#deferred?.resolve();
223236
} else {
224-
// otherwise mark effects clean so they get scheduled on the next run
225-
for (const e of this.#render_effects) set_signal_status(e, CLEAN);
226-
for (const e of this.#effects) set_signal_status(e, CLEAN);
227-
for (const e of this.#block_effects) set_signal_status(e, CLEAN);
237+
this.#defer_effects(this.#render_effects);
238+
this.#defer_effects(this.#effects);
239+
this.#defer_effects(this.#block_effects);
228240
}
229241

230242
if (current_values) {
@@ -307,6 +319,21 @@ export class Batch {
307319
}
308320
}
309321

322+
/**
323+
* @param {Effect[]} effects
324+
*/
325+
#defer_effects(effects) {
326+
for (const e of effects) {
327+
const target = (e.f & DIRTY) !== 0 ? this.#dirty_effects : this.#maybe_dirty_effects;
328+
target.push(e);
329+
330+
// mark as clean so they get scheduled if they depend on pending async state
331+
set_signal_status(e, CLEAN);
332+
}
333+
334+
effects.length = 0;
335+
}
336+
310337
/**
311338
* Associate a change to a given source with the current
312339
* batch, noting its previous and current values
@@ -384,18 +411,13 @@ export class Batch {
384411
this.#pending -= 1;
385412

386413
if (this.#pending === 0) {
387-
for (const e of this.#render_effects) {
414+
for (const e of this.#dirty_effects) {
388415
set_signal_status(e, DIRTY);
389416
schedule_effect(e);
390417
}
391418

392-
for (const e of this.#effects) {
393-
set_signal_status(e, DIRTY);
394-
schedule_effect(e);
395-
}
396-
397-
for (const e of this.#block_effects) {
398-
set_signal_status(e, DIRTY);
419+
for (const e of this.#maybe_dirty_effects) {
420+
set_signal_status(e, MAYBE_DIRTY);
399421
schedule_effect(e);
400422
}
401423

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { tick } from 'svelte';
2+
import { test } from '../../test';
3+
4+
export default test({
5+
async test({ assert, target, logs }) {
6+
await tick();
7+
8+
const [increment] = target.querySelectorAll('button');
9+
10+
assert.deepEqual(logs, [false]);
11+
assert.htmlEqual(target.innerHTML, '<button>increment</button><p>0</p>');
12+
13+
increment.click();
14+
await tick();
15+
assert.deepEqual(logs, [false]);
16+
assert.htmlEqual(target.innerHTML, '<button>increment</button><p>1</p>');
17+
18+
increment.click();
19+
await tick();
20+
assert.deepEqual(logs, [false, true]);
21+
assert.htmlEqual(target.innerHTML, '<button>increment</button><p>2</p>');
22+
23+
increment.click();
24+
await tick();
25+
assert.deepEqual(logs, [false, true]);
26+
assert.htmlEqual(target.innerHTML, '<button>increment</button><p>3</p>');
27+
}
28+
});
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<script lang="ts">
2+
let count = $state(0);
3+
let two_or_larger = $derived(count >= 2);
4+
5+
$effect(() => {
6+
console.log(two_or_larger);
7+
});
8+
</script>
9+
10+
<svelte:boundary>
11+
<button onclick={() => count += 1}>increment</button>
12+
<p>{await count}</p>
13+
14+
{#snippet pending()}
15+
<p>loading...</p>
16+
{/snippet}
17+
</svelte:boundary>

0 commit comments

Comments
 (0)