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; 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 {FunctionDeclaration | FunctionExpression | ArrowFunctionExpression} node
* @param {ComponentContext} context * @param {ComponentContext} context

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

@ -109,7 +109,14 @@ export {
user_effect, user_effect,
user_pre_effect user_pre_effect
} from './reactivity/effects.js'; } 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 { export {
prop, prop,
rest_props, rest_props,

@ -206,6 +206,26 @@ export function set(source, value, should_proxy = false, needs_previous = false)
return internal_set(source, new_value); 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 * @template V
* @param {Source<V>} source * @param {Source<V>} source

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

@ -22,6 +22,24 @@
console.log("class proxy"); 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(); const class_test = new Test();

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

@ -22,6 +22,24 @@
console.log("class proxy"); 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(); const class_test = new Test();

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

Loading…
Cancel
Save