Skip to content

Commit d9c250a

Browse files
trueadmdummdidumm
andauthored
fix: better handle unowned derived signals (#9832)
* fix: better handle unowned derived signals * format --------- Co-authored-by: Simon H <[email protected]>
1 parent b20b461 commit d9c250a

File tree

5 files changed

+118
-0
lines changed

5 files changed

+118
-0
lines changed

.changeset/ten-foxes-repeat.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: better handle unowned derived signals

packages/svelte/src/internal/client/runtime.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -392,6 +392,8 @@ function remove_consumer(signal, start_index, remove_unowned) {
392392
}
393393
}
394394
if (remove_unowned && consumers_length === 0 && (dependency.f & UNOWNED) !== 0) {
395+
// If the signal is unowned then we need to make sure to change it to dirty.
396+
set_signal_status(dependency, DIRTY);
395397
remove_consumer(
396398
/** @type {import('./types.js').ComputationSignal<V>} **/ (dependency),
397399
0,
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<script context="module">
2+
class Foo {
3+
x = $state(5);
4+
y = $derived(this.x * 2);
5+
}
6+
const foo = new Foo();
7+
8+
let x = $state(2);
9+
let y = $derived(x * 2);
10+
const bar = {
11+
get x() {
12+
return x;
13+
},
14+
set x(val) {
15+
x = val;
16+
},
17+
18+
get y() {
19+
return y;
20+
},
21+
};
22+
</script>
23+
24+
<button onclick={() => foo.x++}>
25+
x: {foo.x}, y: {foo.y}
26+
</button>
27+
28+
<button onclick={() => bar.x++}>
29+
x: {bar.x}, y: {bar.y}
30+
</button>
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import { flushSync } from 'svelte';
2+
import { test } from '../../test';
3+
4+
export default test({
5+
skip_if_hydrate: 'permanent',
6+
async test({ assert, target }) {
7+
let [btn1, btn2] = target.querySelectorAll('button');
8+
const input = target.querySelector('input');
9+
10+
flushSync(() => {
11+
btn1?.click();
12+
});
13+
14+
assert.htmlEqual(
15+
target.innerHTML,
16+
`<input type="checkbox"><button>x:
17+
6,
18+
y:
19+
12</button><button>x:
20+
2,
21+
y:
22+
4</button>`
23+
);
24+
25+
flushSync(() => {
26+
btn2?.click();
27+
});
28+
29+
assert.htmlEqual(
30+
target.innerHTML,
31+
`<input type="checkbox"><button>x:
32+
6,
33+
y:
34+
12</button><button>x:
35+
3,
36+
y:
37+
6</button>`
38+
);
39+
40+
flushSync(() => {
41+
input?.click();
42+
});
43+
44+
assert.htmlEqual(target.innerHTML, `<input type="checkbox">`);
45+
46+
flushSync(() => {
47+
input?.click();
48+
});
49+
50+
[btn1, btn2] = target.querySelectorAll('button');
51+
52+
flushSync(() => {
53+
btn1?.click();
54+
});
55+
56+
flushSync(() => {
57+
btn2?.click();
58+
});
59+
60+
assert.htmlEqual(
61+
target.innerHTML,
62+
`<input type="checkbox"><button>x:
63+
7,
64+
y:
65+
14</button><button>x:
66+
4,
67+
y:
68+
8</button>`
69+
);
70+
}
71+
});
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<script>
2+
import Component from "./Component.svelte";
3+
4+
let show = $state(true);
5+
</script>
6+
7+
<input type="checkbox" bind:checked={show} />
8+
{#if show}
9+
<Component/>
10+
{/if}

0 commit comments

Comments
 (0)