fix: invoke `onchange` in component constructor

pull/15579/head
paoloricciuti 8 months ago
parent 1fb57ebe16
commit 4ed4351c54

@ -45,16 +45,6 @@ export function build_getter(node, state) {
return node;
}
/**
* @param {Expression} value
* @param {Expression} previous
*/
export function build_proxy_reassignment(value, previous) {
return dev
? b.call('$.proxy', value, b.call('$.get_options', previous), b.null, previous)
: b.call('$.proxy', value, b.call('$.get_options', previous));
}
/**
* @param {FunctionDeclaration | FunctionExpression | ArrowFunctionExpression} node
* @param {ComponentContext} context

@ -9,7 +9,7 @@ import {
is_event_attribute
} from '../../../../utils/ast.js';
import { dev, filename, is_ignored, locate_node, locator } from '../../../../state.js';
import { build_proxy_reassignment, should_proxy } from '../utils.js';
import { should_proxy } from '../utils.js';
import { visit_assignment_expression } from '../../shared/assignments.js';
/**
@ -70,18 +70,16 @@ function build_assignment(operator, left, right, context) {
is_non_coercive_operator(operator) &&
should_proxy(value, context.state.scope);
if (context.state.in_constructor) {
// inside the constructor, we can assign to `this.#foo.v` rather than using `$.set`,
// since nothing is tracking the signal at this point
return b.assignment(
operator,
/** @type {Pattern} */ (context.visit(left)),
needs_proxy ? build_proxy_reassignment(value, b.member(b.this, private_state.id)) : value
return b.call(
// inside the constructor, we use `$.simple_set` rather than using `$.set`,
// that only assign the value and eventually call onchange since nothing is tracking the signal at this point
context.state.in_constructor ? '$.simple_set' : '$.set',
left,
value,
needs_proxy && b.true,
dev && needs_proxy && b.true
);
}
return b.call('$.set', left, value, needs_proxy && b.true, dev && needs_proxy && b.true);
}
}
let object = left;

@ -109,7 +109,14 @@ export {
user_effect,
user_pre_effect
} from './reactivity/effects.js';
export { mutable_state, mutate, set, state, get_options } from './reactivity/sources.js';
export {
mutable_state,
mutate,
set,
simple_set,
state,
get_options
} from './reactivity/sources.js';
export {
prop,
rest_props,

@ -206,6 +206,26 @@ export function set(source, value, should_proxy = false, needs_previous = false)
return internal_set(source, new_value);
}
/**
* @template V
* @param {Source<V>} source
* @param {V} value
* @param {boolean} [should_proxy]
* @param {boolean} [needs_previous]
* @returns {V}
*/
export function simple_set(source, value, should_proxy = false, needs_previous = false) {
let new_value = should_proxy
? needs_previous
? proxy(value, source.o, null, source)
: proxy(value, source.o)
: value;
source.v = new_value;
return new_value;
}
/**
* @template V
* @param {Source<V>} source

@ -4,6 +4,11 @@ import { test } from '../../test';
export default test({
async test({ assert, target, logs }) {
const [btn, btn2, btn3, btn4, btn5, btn6, btn7, btn8, btn9] = target.querySelectorAll('button');
assert.deepEqual(logs, ['constructor count', 'constructor proxy']);
logs.length = 0;
flushSync(() => {
btn.click();
});

@ -22,6 +22,24 @@
console.log("class proxy");
}
})
#in_constructor = $state(0, {
onchange(){
console.log("constructor count");
}
});
#in_constructor_proxy = $state({ count: 0 }, {
onchange(){
console.log("constructor proxy");
}
});
constructor(){
this.#in_constructor++;
this.#in_constructor_proxy.count++;
}
}
const class_test = new Test();

@ -5,6 +5,11 @@ export default test({
async test({ assert, target, logs }) {
const [btn, btn2, btn3, btn4, btn5, btn6, btn7, btn8, btn9, btn10] =
target.querySelectorAll('button');
assert.deepEqual(logs, ['constructor count', 'constructor proxy']);
logs.length = 0;
flushSync(() => {
btn.click();
});

@ -22,6 +22,24 @@
console.log("class proxy");
}
})
#in_constructor = $state(0, {
onchange(){
console.log("constructor count");
}
});
#in_constructor_proxy = $state({ count: 0 }, {
onchange(){
console.log("constructor proxy");
}
});
constructor(){
this.#in_constructor++;
this.#in_constructor_proxy.count++;
}
}
const class_test = new Test();

@ -19,7 +19,7 @@ export default function Class_state_field_constructor_assignment($$anchor, $$pro
constructor() {
this.a = 1;
this.#b.v = 2;
$.simple_set(this.#b, 2);
}
}

Loading…
Cancel
Save