Litの「change-in-update」警告を複数コンポーネントで抑制・解消

shoelace-style/webawesome

Web Awesomeの複数コンポーネントで、Litの開発モード実行時に発生していた「scheduled an update after an update completed」警告を、根本原因の分析にもとづき適切な手段で解消した。

背景

Issue #1269 として報告されていたこの問題では、wa-buttonwa-checkbox など多くのコンポーネントが、開発モードのLitランタイムから change-in-update 警告を発していた。同等の機能を持つShoelaceコンポーネントでは発生しないにもかかわらず、Web Awesomeの実装では初期レンダリング時に必ず警告が出る状態だった。

警告の本質は「ある更新サイクルが完了した後に、別の更新サイクルがスケジュールされている」という非効率な動作の検出にある。Web Awesomeでは主に2つのパターンがこの問題を引き起こしていた。1つ目は、WebAwesomeFormAssociatedElement 基底クラスの firstUpdated() 内で呼ばれる updateValidity()requestUpdate('validity') をトリガーするパターン。2つ目は、HasSlotController がスロットの変化(slotchangeイベント)を検知して requestUpdate() を呼び出すパターンだ。これらはいずれも「DOMが利用可能になって初めて状態を確定できる」という本質的な制約から生じている。

これらの更新は機能的には必要なものであり、警告の原因を取り除くことは設計上困難であることから、今回の修正では問題の種類に応じて異なる対処方針が採られた。

技術的な変更

今回の修正は大きく3つのアプローチに分類できる。

disableWarning('change-in-update') の追加(多数のコンポーネント)

最も多く採用されたアプローチは、Litが提供する disableWarning 静的メソッドを使って警告を明示的に抑制するものだ。対象コンポーネントは以下のとおり。

  • wa-button
  • wa-checkbox
  • wa-color-picker
  • wa-input
  • wa-number-input
  • wa-progress-ring
  • wa-radio
  • wa-radio-group
  • wa-select
  • wa-switch
  • wa-textarea
  • wa-tree-item

各ファイルのクラス定義直後に以下のような1行が追加されている。

WaButton.disableWarning?.('change-in-update');

オプショナルチェーン(?.)を使用しているのは、disableWarning が開発ビルドのみで利用可能なAPIであるためだ。本番ビルドではこのメソッドが存在しないため、?. により安全に無視される。各コンポーネントには詳細なコメントが付記されており、なぜその警告が不可避なのかが説明されている。

updated() から willUpdate() へのリファクタリング(wa-cardwa-slider

一部のコンポーネントでは、updated() 内で状態プロパティを変更していたことが警告の原因だったため、より適切なライフサイクルメソッドへ移行した。

wa-card では、スロットの存在チェックにもとづいて withHeaderwithMedia プロパティを更新するロジックが updated() から willUpdate() に移動された。

// 変更前
updated() {
  if (!this.withHeader && this.hasSlotController.test('header')) this.withHeader = true;
  if (!this.withMedia && this.hasSlotController.test('media')) this.withMedia = true;
  // ...
}

// 変更後
willUpdate() {
  if (!this.withHeader && this.hasSlotController.test('header')) this.withHeader = true;
  if (!this.withMedia && this.hasSlotController.test('media')) this.withMedia = true;
  // ...
}

wa-slider はより大規模なリファクタリングが行われた。updated() に散在していた range 変更時の requestUpdate() 呼び出し、min/max 変更時のクランプ処理などが willUpdate() に集約された。また、min/max の変更に対してもクランプ処理が適用されるよう、changedProperties.has('min')changedProperties.has('max') の条件チェックが追加されている。

③ ビルド設定への conditions 追加(build.js

disableWarning が機能するためには、Litの開発ビルドが読み込まれている必要がある。packages/webawesome/scripts/build.js に以下の変更が加えられ、開発時には development エクスポート条件が有効化されるようになった。

const config = {
  conditions: isDeveloping ? ['development'] : [],
  format: 'esm',
  // ...
};

これにより、isDeveloping フラグが true の場合に限りLitの開発ビルドが選択され、disableWarning APIを含む開発用の機能が利用可能になる。

設計判断

警告の抑制と根本的な解消を使い分けた点がこの修正の核心にある。

willUpdate() への移行は、更新サイクル内で状態変更を完結させる「正しい修正」だ。しかし wa-progress-ring のように、updated() でしか実行できない処理(DOMが描画された後でなければ getComputedStyle() が取得できないSafariのワークアラウンド)や、フォームバリデーションのように「レンダリング後に検証ターゲットを参照する必要がある」処理では、この手法は使えない。そうしたケースに対しては disableWarning を選択している。

重要なのは、disableWarning の使用が「警告を黙らせる」だけでなく、詳細なコードコメントを通じて「なぜこの警告が不可避なのか」を明示的にドキュメント化している点だ。将来のメンテナが警告の意図を誤解して削除したり、逆に不必要な最適化を試みるリスクを低減する設計になっている。

disableWarning?.() のオプショナルチェーンは、同一のソースコードが開発・本番両ビルドで動作するための必須の配慮であり、ビルドフラグによる条件分岐なしに両環境を透過的にサポートする。

まとめ

本PRは、Litの更新ライフサイクルに関する深い理解をもとに、「修正可能な箇所は willUpdate() で修正し、構造的に不可避な箇所は根拠を明示した上で disableWarning で抑制する」という一貫した方針を採った。開発者体験のノイズを除去しつつ、設計の意図をコードベースに記録した実用的な修正といえる。

記事メタデータ

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

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

品質レビュー結果

Review Status:
承認済み
Review Count:
1回
Reviewed by:
Gemini 2.5 Pro for DiffDaily

Review Criteria:

記事構成 ✓ PASS

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

リード文(総論)→セクション群(背景、技術的変更、設計判断)(各論)→まとめ(結論)という3部構成が明確に適用されており、ガイドラインに完全に準拠しています。

カスタムMarkdown構文 ✓ PASS

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

ファイル名付きシンタックスハイライト(```言語:ファイルパス)、GitHubのIssue/PRへのリンク記法がすべて正しく使用されています。

対象読者への適合性 ✓ PASS

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

Litのライフサイクルやコンポーネント開発に関する専門用語が適切に使用されており、専門知識を持つエンジニアという対象読者に適合した内容です。

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

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

各セクションが総論→各論の構成になっており、各段落がトピックセンテンスで始まるなど、パラグラフ・ライティングの原則がよく守られています。非常に読みやすい構成です。

Diff内容との照合 ⚠ WARNING

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

`wa-card`の変更について`willUpdate()`への移行のみが言及されていますが、実際のDiffでは`disableWarning()`も追加されています。説明の単純化と思われますが、Diffの内容を完全に反映してはいません。

技術用語の正確性 ✓ PASS

技術用語の正確な使用

「change-in-update」「willUpdate」「HasSlotController」など、Litに関する技術用語が文脈に応じて正確に使用されています。

説明の技術的正確性 ✓ PASS

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

Litのライフサイクル(`updated`と`willUpdate`の違い)、`disableWarning`の役割、ビルド設定の`conditions`の意図など、技術的な説明は正確で論理的です。

事実の突合 ✓ PASS

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

記事の主張はすべてPRのDiff(コードとコメント)、またはリンクされたIssue情報で裏付けられており、ハルシネーション(創作)は見られません。

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

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

PR番号「#2161」、Issue番号「#1269」などの数値や固有名詞はすべて正確に記載されています。

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

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

記事のタイトル「Litの「change-in-update」警告を複数コンポーネントで抑制・解消」は、PRの内容を具体的かつ正確に表現しています。

外部知識の正確性 ✓ PASS

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

PR情報に基づかないバージョンサポート状況やリリース日程などの外部知識の追加はなく、PRの範囲内で説明が完結しています。

時間表現の正確性 ✓ PASS

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

「発生していた」「解消した」といった時間表現は、過去の問題を修正したというPRの文脈と一致しており、歪曲はありません。