Skip to content

Commit 32481be

Browse files
authored
chore: simplify <select> initialization (#16251)
* initialize option values before initing select values * simplify init_select * simplify * tweak * tidy up * tweak * on second thoughts just simplify it here
1 parent 213274a commit 32481be

File tree

3 files changed

+50
-63
lines changed

3 files changed

+50
-63
lines changed

packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js

Lines changed: 22 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,7 @@ import {
2222
build_set_style
2323
} from './shared/element.js';
2424
import { process_children } from './shared/fragment.js';
25-
import {
26-
build_render_statement,
27-
build_template_chunk,
28-
get_expression_id,
29-
memoize_expression
30-
} from './shared/utils.js';
25+
import { build_render_statement, build_template_chunk, get_expression_id } from './shared/utils.js';
3126
import { visit_event_attribute } from './shared/events.js';
3227

3328
/**
@@ -199,24 +194,23 @@ export function RegularElement(node, context) {
199194

200195
const node_id = context.state.node;
201196

197+
/** If true, needs `__value` for inputs */
198+
const needs_special_value_handling =
199+
node.name === 'option' ||
200+
node.name === 'select' ||
201+
bindings.has('group') ||
202+
bindings.has('checked');
203+
202204
if (has_spread) {
203205
build_attribute_effect(attributes, class_directives, style_directives, context, node, node_id);
204206
} else {
205-
/** If true, needs `__value` for inputs */
206-
const needs_special_value_handling =
207-
node.name === 'option' ||
208-
node.name === 'select' ||
209-
bindings.has('group') ||
210-
bindings.has('checked');
211-
212207
for (const attribute of /** @type {AST.Attribute[]} */ (attributes)) {
213208
if (is_event_attribute(attribute)) {
214209
visit_event_attribute(attribute, context);
215210
continue;
216211
}
217212

218213
if (needs_special_value_handling && attribute.name === 'value') {
219-
build_element_special_value_attribute(node.name, node_id, attribute, context);
220214
continue;
221215
}
222216

@@ -391,6 +385,15 @@ export function RegularElement(node, context) {
391385
context.state.update.push(b.stmt(b.assignment('=', dir, dir)));
392386
}
393387

388+
if (!has_spread && needs_special_value_handling) {
389+
for (const attribute of /** @type {AST.Attribute[]} */ (attributes)) {
390+
if (attribute.name === 'value') {
391+
build_element_special_value_attribute(node.name, node_id, attribute, context);
392+
break;
393+
}
394+
}
395+
}
396+
394397
context.state.template.pop_element();
395398
}
396399

@@ -621,12 +624,7 @@ function build_element_special_value_attribute(element, node_id, attribute, cont
621624
element === 'select' && attribute.value !== true && !is_text_attribute(attribute);
622625

623626
const { value, has_state } = build_attribute_value(attribute.value, context, (value, metadata) =>
624-
metadata.has_call
625-
? // if is a select with value we will also invoke `init_select` which need a reference before the template effect so we memoize separately
626-
is_select_with_value
627-
? memoize_expression(state, value)
628-
: get_expression_id(state.expressions, value)
629-
: value
627+
metadata.has_call ? get_expression_id(state.expressions, value) : value
630628
);
631629

632630
const evaluated = context.state.scope.evaluate(value);
@@ -651,10 +649,6 @@ function build_element_special_value_attribute(element, node_id, attribute, cont
651649
: inner_assignment
652650
);
653651

654-
if (is_select_with_value) {
655-
state.init.push(b.stmt(b.call('$.init_select', node_id, b.thunk(value))));
656-
}
657-
658652
if (has_state) {
659653
const id = b.id(state.scope.generate(`${node_id.name}_value`));
660654

@@ -668,4 +662,8 @@ function build_element_special_value_attribute(element, node_id, attribute, cont
668662
} else {
669663
state.init.push(update);
670664
}
665+
666+
if (is_select_with_value) {
667+
state.init.push(b.stmt(b.call('$.init_select', node_id)));
668+
}
671669
}

packages/svelte/src/internal/client/dom/elements/attributes.js

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import { clsx } from '../../../shared/attributes.js';
2020
import { set_class } from './class.js';
2121
import { set_style } from './style.js';
2222
import { ATTACHMENT_KEY, NAMESPACE_HTML } from '../../../../constants.js';
23-
import { block, branch, destroy_effect } from '../../reactivity/effects.js';
23+
import { block, branch, destroy_effect, effect } from '../../reactivity/effects.js';
2424
import { derived } from '../../reactivity/deriveds.js';
2525
import { init_select, select_option } from './bindings/select.js';
2626

@@ -513,10 +513,12 @@ export function attribute_effect(
513513
});
514514

515515
if (is_select) {
516-
init_select(
517-
/** @type {HTMLSelectElement} */ (element),
518-
() => /** @type {Record<string | symbol, any>} */ (prev).value
519-
);
516+
var select = /** @type {HTMLSelectElement} */ (element);
517+
518+
effect(() => {
519+
select_option(select, /** @type {Record<string | symbol, any>} */ (prev).value);
520+
init_select(select);
521+
});
520522
}
521523

522524
inited = true;

packages/svelte/src/internal/client/dom/elements/bindings/select.js

Lines changed: 21 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
1-
import { effect } from '../../../reactivity/effects.js';
1+
import { effect, teardown } from '../../../reactivity/effects.js';
22
import { listen_to_event_and_reset_event } from './shared.js';
3-
import { untrack } from '../../../runtime.js';
43
import { is } from '../../../proxy.js';
54
import { is_array } from '../../../../shared/utils.js';
65
import * as w from '../../../warnings.js';
@@ -51,40 +50,29 @@ export function select_option(select, value, mounting) {
5150
* current selection to the dom when it changes. Such
5251
* changes could for example occur when options are
5352
* inside an `#each` block.
54-
* @template V
5553
* @param {HTMLSelectElement} select
56-
* @param {() => V} [get_value]
5754
*/
58-
export function init_select(select, get_value) {
59-
let mounting = true;
60-
effect(() => {
61-
if (get_value) {
62-
select_option(select, untrack(get_value), mounting);
63-
}
64-
mounting = false;
55+
export function init_select(select) {
56+
var observer = new MutationObserver(() => {
57+
// @ts-ignore
58+
select_option(select, select.__value);
59+
// Deliberately don't update the potential binding value,
60+
// the model should be preserved unless explicitly changed
61+
});
62+
63+
observer.observe(select, {
64+
// Listen to option element changes
65+
childList: true,
66+
subtree: true, // because of <optgroup>
67+
// Listen to option element value attribute changes
68+
// (doesn't get notified of select value changes,
69+
// because that property is not reflected as an attribute)
70+
attributes: true,
71+
attributeFilter: ['value']
72+
});
6573

66-
var observer = new MutationObserver(() => {
67-
// @ts-ignore
68-
var value = select.__value;
69-
select_option(select, value);
70-
// Deliberately don't update the potential binding value,
71-
// the model should be preserved unless explicitly changed
72-
});
73-
74-
observer.observe(select, {
75-
// Listen to option element changes
76-
childList: true,
77-
subtree: true, // because of <optgroup>
78-
// Listen to option element value attribute changes
79-
// (doesn't get notified of select value changes,
80-
// because that property is not reflected as an attribute)
81-
attributes: true,
82-
attributeFilter: ['value']
83-
});
84-
85-
return () => {
86-
observer.disconnect();
87-
};
74+
teardown(() => {
75+
observer.disconnect();
8876
});
8977
}
9078

@@ -136,7 +124,6 @@ export function bind_select_value(select, get, set = get) {
136124
mounting = false;
137125
});
138126

139-
// don't pass get_value, we already initialize it in the effect above
140127
init_select(select);
141128
}
142129

0 commit comments

Comments
 (0)