Skip to content

Commit 562f23a

Browse files
committed
invalidate dependencies of reactive declarations - fixes sveltejs#2444
1 parent 43f82af commit 562f23a

File tree

3 files changed

+169
-3
lines changed

3 files changed

+169
-3
lines changed

src/compile/Component.ts

+19-3
Original file line numberDiff line numberDiff line change
@@ -753,18 +753,34 @@ export default class Component {
753753
});
754754
}
755755

756-
invalidate(name, value = name) {
756+
invalidate(name, value) {
757757
const variable = this.var_lookup.get(name);
758758

759759
if (variable && (variable.subscribable && variable.reassigned)) {
760-
return `$$subscribe_${name}(), $$invalidate('${name}', ${value})`;
760+
return `$$subscribe_${name}(), $$invalidate('${name}', ${value || name})`;
761761
}
762762

763763
if (name[0] === '$' && name[1] !== '$') {
764764
return `${name.slice(1)}.set(${name})`
765765
}
766766

767-
return `$$invalidate('${name}', ${value})`;
767+
if (value) {
768+
return `$$invalidate('${name}', ${value})`;
769+
}
770+
771+
// if this is a reactive declaration, invalidate dependencies recursively
772+
const deps = new Set([name]);
773+
774+
deps.forEach(name => {
775+
const reactive_declarations = this.reactive_declarations.filter(x => x.assignees.has(name));
776+
reactive_declarations.forEach(declaration => {
777+
declaration.dependencies.forEach(name => {
778+
deps.add(name);
779+
});
780+
});
781+
});
782+
783+
return Array.from(deps).map(n => `$$invalidate('${n}', ${n})`).join(', ');
768784
}
769785

770786
rewrite_props(get_insert: (variable: Var) => string) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
export default {
2+
props: {
3+
items: [
4+
{ done: false, text: 'one' },
5+
{ done: true, text: 'two' },
6+
{ done: false, text: 'three' }
7+
]
8+
},
9+
10+
html: `
11+
<div>
12+
<input type="checkbox">
13+
<input type="text"><p>one</p>
14+
</div>
15+
<div>
16+
<input type="checkbox">
17+
<input type="text"><p>two</p>
18+
</div>
19+
<div>
20+
<input type="checkbox">
21+
<input type="text"><p>three</p>
22+
</div>
23+
24+
<p>remaining:one / done:two / remaining:three</p>
25+
`,
26+
27+
ssrHtml: `
28+
<div>
29+
<input type="checkbox">
30+
<input type="text" value=one><p>one</p>
31+
</div>
32+
<div>
33+
<input type="checkbox" checked="">
34+
<input type="text" value=two><p>two</p>
35+
</div>
36+
<div>
37+
<input type="checkbox">
38+
<input type="text" value=three><p>three</p>
39+
</div>
40+
41+
<p>remaining:one / done:two / remaining:three</p>
42+
`,
43+
44+
async test({ assert, component, target, window }) {
45+
function set_text(i, text) {
46+
const input = target.querySelectorAll('input[type="text"]')[i];
47+
input.value = text;
48+
input.dispatchEvent(new window.Event('input'));
49+
}
50+
51+
function set_done(i, done) {
52+
const input = target.querySelectorAll('input[type="checkbox"]')[i];
53+
input.checked = done;
54+
input.dispatchEvent(new window.Event('change'));
55+
}
56+
57+
component.filter = 'remaining';
58+
59+
assert.htmlEqual(target.innerHTML, `
60+
<div>
61+
<input type="checkbox">
62+
<input type="text"><p>one</p>
63+
</div>
64+
<div>
65+
<input type="checkbox">
66+
<input type="text"><p>three</p>
67+
</div>
68+
69+
<p>remaining:one / done:two / remaining:three</p>
70+
`);
71+
72+
await set_text(1, 'four');
73+
74+
assert.htmlEqual(target.innerHTML, `
75+
<div>
76+
<input type="checkbox">
77+
<input type="text"><p>one</p>
78+
</div>
79+
<div>
80+
<input type="checkbox">
81+
<input type="text"><p>four</p>
82+
</div>
83+
84+
<p>remaining:one / done:two / remaining:four</p>
85+
`);
86+
87+
assert.deepEqual(component.items, [
88+
{ done: false, text: 'one' },
89+
{ done: true, text: 'two' },
90+
{ done: false, text: 'four' }
91+
]);
92+
93+
await set_done(0, true);
94+
95+
assert.htmlEqual(target.innerHTML, `
96+
<div>
97+
<input type="checkbox">
98+
<input type="text"><p>four</p>
99+
</div>
100+
101+
<p>done:one / done:two / remaining:four</p>
102+
`);
103+
104+
assert.deepEqual(component.items, [
105+
{ done: true, text: 'one' },
106+
{ done: true, text: 'two' },
107+
{ done: false, text: 'four' }
108+
]);
109+
110+
component.filter = 'done';
111+
112+
assert.htmlEqual(target.innerHTML, `
113+
<div>
114+
<input type="checkbox">
115+
<input type="text"><p>one</p>
116+
</div>
117+
<div>
118+
<input type="checkbox">
119+
<input type="text"><p>two</p>
120+
</div>
121+
122+
<p>done:one / done:two / remaining:four</p>
123+
`);
124+
},
125+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<script>
2+
export let items;
3+
export let filter = 'all';
4+
5+
$: done = items.filter(item => item.done);
6+
$: remaining = items.filter(item => !item.done);
7+
8+
$: filtered = (
9+
filter === 'all' ? items :
10+
filter === 'done' ? done :
11+
remaining
12+
);
13+
14+
$: summary = items.map(i => `${i.done ? 'done' : 'remaining'}:${i.text}`).join(' / ');
15+
</script>
16+
17+
{#each filtered as item}
18+
<div>
19+
<input type="checkbox" bind:checked={item.done}>
20+
<input type="text" bind:value={item.text}>
21+
<p>{item.text}</p>
22+
</div>
23+
{/each}
24+
25+
<p>{summary}</p>

0 commit comments

Comments
 (0)