`wa-select` / `wa-combobox` の初期値が表示されないバグを修正

shoelace-style/webawesome

オプションがDOMに追加される前にプロパティ経由で値を設定した場合に選択値がUIに反映されなかった問題が解消されます。value セッターの変更検知ロジックを修正することで、DOMの準備状態に依存しない堅牢な初期値表示を実現しました。

背景

<wa-select>value プロパティでページロード時の初期値を設定しても、UIに選択値が表示されないバグが報告されました(#2253)。値はコード上は正しく設定されているにもかかわらず、UIには反映されないという挙動でした。

Issue の報告では、コンポーネントのプロパティを設定してから子要素をDOMに追加するというライフサイクルパターンで問題が発生することが示されています。つまり、el.value = 'option-2' を実行した時点では <wa-option> がまだDOMに存在しない状態となり、初期値が正しく表示されません。なお、Issue によれば multiple 属性付きの <wa-select> では同現象は確認されていませんでした。

技術的な変更

select.tsvalue セッターにおける変更検知ロジックが修正されました。問題の核心は、optionValues(有効なオプションの値を保持するキャッシュ)が空の状態で value プロパティが設定された際に、変更として検知されなかったことにあります。

変更前のセッターは、変更検知に value ゲッターが返す値を比較していました。value ゲッターは optionValues を通じてフィルタリングを行うため、オプションがDOMに存在しない状態では optionValues が空となり、設定した値が「無効」と判断されて変更なしと見なされていました。

変更後は、ゲッターを介さずに内部のrawな値を直接比較するよう修正されています。新たに追加された rawValuesEqual メソッドが配列の順序まで含めた厳密な等価比較を担い、セッター内の変更検知はこのメソッドを利用するよう変更されました。

private rawValuesEqual(a: string[] | null | undefined, b: string[] | null | undefined): boolean {
  if (a == null && b == null) return true;
  if (a == null || b == null) return false;
  if (a.length !== b.length) return false;
  return a.every((v, i) => v === b[i]);
}

セッター内の比較部分は以下のように変更されています。

// 変更前
let newValue = this.value;

if (newValue !== oldValue) {
  this.valueHasChanged = true;
  this.requestUpdate('value', oldValue);
}

// 変更後
const oldRawValue = this._value;
this._value = val ?? null;

// Compare raw internal values to detect actual changes. We can't rely on the getter because it filters through
// optionValues, which may be empty when options aren't in the DOM yet (common with frameworks that set properties
// before appending children).
if (!this.rawValuesEqual(oldRawValue, this._value)) {
  this.valueHasChanged = true;
  this.requestUpdate('value', oldValue);
}

また、optionValues プロパティから @state() デコレータが除去され、/** @internal */ JSDocが付与されました。これにより、optionValues の変更が不要なリアクティブな再レンダリングを引き起こさないようになっています。テストでは、値を先に設定してからオプションをDOMに追加するシナリオが追加されており、シングル選択・複数選択の両ケースがカバーされています。

設計判断

rawな内部値(_value)を比較する方式 が採用されました。

コード内コメントでも明示されているように、value ゲッターを通じた比較は optionValues によるフィルタリングに依存するため、オプションが未追加の状態では正確な変更検知ができません。内部状態である _value を直接比較することで、DOMの準備状態に依存しない変更検知を実現しています。等価比較ロジックを rawValuesEqual メソッドとして切り出すことで、変更の意図が明確になっています。

optionValues@state() 除去も同じ思想の延長です。このキャッシュはUIレンダリングとは独立した内部最適化であり、リアクティブな更新トリガーとして機能すべきではありませんでした。/** @internal */ タグを付与することでパブリックAPIではないことを明示しつつ、不要な再レンダリングを抑制しています。

まとめ

本修正は、「プロパティ設定 → 子要素追加」というライフサイクルパターンに起因する変更検知の不具合を、最小限のコード変更で解消しています。value ゲッターとrawな内部値(_value)の責務を明確に分離したことで、オプションのDOM状態に依存しない堅牢な変更検知が実現されました。

記事メタデータ

Generated by:
Claude Sonnet 4.6 for DiffDaily
LLM Trace:
8ecaa6cb

この記事はAIによって自動生成されています。内容の正確性については、必ずソースコードやPRを確認してください。

品質レビュー結果

Review Status:
リトライ後承認
Review Count:
2回 (改善を経て承認)
Reviewed by:
Gemini 2.5 Pro for DiffDaily

Review Criteria:

記事構成 ✓ PASS

Title, Context, Technical Detailの存在と明確さ

リード文(総論)、背景、技術的な変更、設計判断、まとめ(結論)という「総論→各論→結論」の構成が明確で、非常に分かりやすいです。

カスタムMarkdown構文 ✓ PASS

シンタックスハイライト・GitHubリンク記法の正確性

ファイル名付きのシンタックスハイライト、Issue番号のリンク記法など、カスタムMarkdown構文がすべて正しく使用されています。

対象読者への適合性 ✓ PASS

エンジニア向けの適切な技術レベルと表現

DOMライフサイクル、変更検知、リアクティブな更新など、専門知識を持つエンジニアを対象とした適切な技術レベルと表現で書かれています。

パラグラフ・ライティング ✓ PASS

トピックセンテンス・1段落1トピック・段落長

各セクション、各パラグラフが論理的に構成されています。特に各段落の1文目が要点を伝えるトピックセンテンスとして機能しており、高い可読性を実現しています。

Diff内容との照合 ⚠ WARNING

コードブロックとDiff内容の一致

Diffの内容を概ね正確に反映していますが、「技術的な変更」セクションの「変更前」のコードはDiffから直接引用したものではなく、説明のために再構成されたものです。ただし、これは変更の意図を明確にするための有益な編集です。

技術用語の正確性 ✓ PASS

技術用語の正確な使用

「valueセッター」「@state()デコレータ」「リアクティブな再レンダリング」など、技術用語が正確かつ文脈に即して適切に使用されています。

説明の技術的正確性 ✓ PASS

技術的主張の正確性と論理性

「optionValuesが空の状態で変更検知されなかった」という問題の核心や、「@state()デコレータの除去による再レンダリング抑制」など、技術的な説明が正確かつ論理的です。

事実の突合 ✓ PASS

PR情報による主張の裏付け(ハルシネーション検出)

記事内のすべての主張(バグの内容、コード変更、テスト追加など)は、PRのDescriptionやDiff内のコード、コメントによって完全に裏付けられています。

数値・固有名詞の確認 ✓ PASS

PR番号・コミットID・バージョン等の正確性

PR番号(#2260)、Issue番号(#2253)などの数値・固有名詞はすべて正確です。

タイトル・説明との一致 ✓ PASS

記事タイトル・説明とPR内容の一致

記事のタイトル「`wa-select` / `wa-combobox` の初期値が表示されないバグを修正」は、PRのタイトル「Select combobox initial value fix」の内容を正確に反映しています。

外部知識の正確性 ✓ PASS

PRに記載のない外部知識(LTS、サポート状況など)の不使用

記事の内容は提供されたPR情報に限定されており、バージョンサポート状況などのPR外の知識を捏造していません。

時間表現の正確性 ✓ PASS

時間表現がPR情報と一致しているか

「問題が解消されます」といった表現は、PRの適用結果を述べるものであり、時間表現に歪曲や誤りはありません。