fix: notify deriveds of changes to sources inside forks (#17437)

* failing tests

* update tests

* update write versions when committing forks

* remove second test, for now

* changeset

* fix: correctly update writable deriveds inside forks (#17438)

* fix: correctly update writable deriveds inside forks

* tweak

* changeset

* on second thoughts, minimise the diff, and revisit later

* missed a spot
pull/17441/head
Rich Harris 2 weeks ago committed by GitHub
parent 4f49ea8961
commit 7ad5772309
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,5 @@
---
'svelte': patch
---
fix: notify deriveds of changes to sources inside forks

@ -0,0 +1,5 @@
---
'svelte': patch
---
fix: correctly update writable deriveds inside forks

@ -25,6 +25,7 @@ import { deferred, define_property } from '../../shared/utils.js';
import {
active_effect,
get,
increment_write_version,
is_dirty,
is_updating_effect,
set_is_updating_effect,
@ -920,13 +921,18 @@ export function fork(fn) {
flushSync(fn);
batch_values = null;
// revert state changes
for (var [source, value] of batch.previous) {
source.v = value;
}
// make writable deriveds dirty, so they recalculate correctly
for (source of batch.current.keys()) {
if ((source.f & DERIVED) !== 0) {
set_signal_status(source, DIRTY);
}
}
return {
commit: async () => {
if (committed) {
@ -942,9 +948,10 @@ export function fork(fn) {
batch.is_fork = false;
// apply changes
// apply changes and update write versions so deriveds see the change
for (var [source, value] of batch.current) {
source.v = value;
source.wv = increment_write_version();
}
// trigger any `$state.eager(...)` expressions with the new state.

@ -0,0 +1,14 @@
import { tick } from 'svelte';
import { test } from '../../test';
export default test({
async test({ assert, target, logs }) {
const [btn] = target.querySelectorAll('button');
btn.click();
await tick();
// d should be 10 (real-world: s=1, d=1*10) before commit, not 20 (fork: s=2, d=2*10)
// After commit, d should be 99 (the written value)
assert.deepEqual(logs, [10, 99]);
}
});

@ -0,0 +1,22 @@
<script>
import { fork } from 'svelte';
let s = $state(1);
let d = $derived(s * 10);
</script>
<button onclick={async () => {
const f = fork(() => {
// First modify s, then write to d
// If d is evaluated in fork context, it would see s=2 and compute d=20
// But it should evaluate in real-world context to get d=10
s = 2;
d = 99;
});
// Should be 10 (real-world value: s=1, so d=1*10=10), not 20 (fork value)
console.log(d);
await f.commit();
// Should be 99 (the value we wrote)
console.log(d);
}}>++</button>

@ -0,0 +1,16 @@
import { tick } from 'svelte';
import { test } from '../../test';
export default test({
async test({ assert, target, logs }) {
const [increment] = target.querySelectorAll('button');
increment.click();
await tick();
assert.deepEqual(logs, [1, 2]);
increment.click();
await tick();
assert.deepEqual(logs, [1, 2, 2, 3]);
}
});

@ -0,0 +1,16 @@
<script>
import { fork } from 'svelte';
let s = $state(1);
let d = $derived(s);
</script>
<button onclick={async () => {
const f = fork(() => {
s = s + 1;
});
console.log(d);
await f.commit();
console.log(d);
}}>++</button>
Loading…
Cancel
Save