fix: ensure assignments to state field inside constructor trigger effects

gh-12084
Rich Harris 3 months ago
parent 803ebd5676
commit 50e41c23be

@ -0,0 +1,5 @@
---
'svelte': patch
---
fix: ensure assignments to state field inside constructor trigger effects

@ -57,20 +57,6 @@ function build_assignment(operator, left, right, context) {
return b.assignment(operator, /** @type {Pattern} */ (context.visit(left)), value);
}
}
} else if (left.property.type === 'Identifier' && context.state.in_constructor) {
const public_state = context.state.public_state.get(left.property.name);
if (public_state !== undefined && should_proxy(right, context.state.scope)) {
const value = /** @type {Expression} */ (context.visit(right));
return b.assignment(
operator,
/** @type {Pattern} */ (context.visit(left)),
public_state.kind === 'raw_state'
? value
: build_proxy_reassignment(value, public_state.id)
);
}
}
}

@ -13,15 +13,6 @@ export function MemberExpression(node, context) {
if (field) {
return context.state.in_constructor ? b.member(node, 'v') : b.call('$.get', node);
}
} else if (node.object.type === 'ThisExpression') {
// rewrite `this.foo` as `this.#foo.v` inside a constructor
if (node.property.type === 'Identifier' && !node.computed) {
const field = context.state.public_state.get(node.property.name);
if (field && context.state.in_constructor) {
return b.member(b.member(b.this, field.id), 'v');
}
}
}
context.next();

@ -0,0 +1,20 @@
import { flushSync } from 'svelte';
import { test } from '../../test';
export default test({
html: `<button>10</button>`,
test({ assert, target, logs }) {
const btn = target.querySelector('button');
btn?.click();
flushSync();
assert.htmlEqual(target.innerHTML, `<button>11</button>`);
btn?.click();
flushSync();
assert.htmlEqual(target.innerHTML, `<button>12</button>`);
assert.deepEqual(logs, [0, 10, 11, 12]);
}
});

@ -0,0 +1,17 @@
<script>
class Counter {
count = $state(0);
constructor(initial) {
$effect.pre(() => {
console.log(this.count);
});
this.count = initial;
}
}
const counter = new Counter(10);
</script>
<button onclick={() => counter.count++}>{counter.count}</button>

@ -1,3 +0,0 @@
import { test } from '../../test';
export default test({});

@ -1,27 +0,0 @@
import "svelte/internal/disclose-version";
import * as $ from "svelte/internal/client";
export default function Class_state_field_constructor_assignment($$anchor, $$props) {
$.push($$props, true);
class Foo {
#a = $.source();
get a() {
return $.get(this.#a);
}
set a(value) {
$.set(this.#a, $.proxy(value));
}
#b = $.source();
constructor() {
this.#a.v = 1;
this.#b.v = 2;
}
}
$.pop();
}

@ -1,17 +0,0 @@
import * as $ from "svelte/internal/server";
export default function Class_state_field_constructor_assignment($$payload, $$props) {
$.push();
class Foo {
a;
#b;
constructor() {
this.a = 1;
this.#b = 2;
}
}
$.pop();
}

@ -1,11 +0,0 @@
<script>
class Foo {
a = $state();
#b = $state();
constructor() {
this.a = 1;
this.#b = 2;
}
}
</script>
Loading…
Cancel
Save