12k
All articles

Refs解説: フレームワークがDOM直接アクセスを処理する方法

React、Vue、Angular、SvelteにおけるDOM refsは、宣言的なパターンが不十分な場面でフレームワークの保証を損なわずに直接DOMへアクセスする手段を提供する。

OpenReplay Team
OpenReplay Team
Refs解説: フレームワークがDOM直接アクセスを処理する方法

モダンなフロントエンドフレームワークは、UIがどのように見えるべきかを記述すれば、残りはフレームワークが処理してくれる宣言的な世界を約束します。しかし、時にはそのモデルから外れて、実際のDOMに触れる必要があります。そこでDOM refsの出番です。

React refs、Vue template refs、Angular ElementRef、Svelte bind:thisのいずれを使用している場合でも、すべての主要なフレームワークはDOM直接アクセスのための脱出ハッチを提供しています。フレームワークの保証を壊すことなく、これらのツールをいつどのように使用するかを理解することが、有能な開発者と、微妙でデバッグが困難な問題を作り出す開発者を分ける境界線となります。

重要なポイント

  • Refsは、宣言的パターンが不十分な場合にDOM直接アクセスのための制御された脱出ハッチを提供します
  • 一般的なユースケースには、フォーカス管理、スクロール、レイアウト測定、サードパーティライブラリの統合が含まれます
  • 各フレームワークはrefsを異なる方法で実装しています:Reactはミュータブルなrefオブジェクトを使用し、VueはdefineExpose()を使用したtemplate refsを使用し、AngularはElementRefRenderer2を提供し、Svelteはbind:thisを使用します
  • Refsはマウント後のクライアント側でのみ存在します—SSR中は常にライフサイクルチェックでアクセスを保護してください

フレームワークがRefsを提供する理由

フレームワークは抽象化レイヤーを通じてDOMを管理します。ReactとVueは仮想DOMを使用します。Angularは変更検知を使用します。Svelteはフレームワーク自体を完全にコンパイルして消去します。これらの抽象化により効率的な更新が可能になりますが、同時にフレームワークがDOM構造を所有することも意味します。

宣言的パターンが不十分な場合、DOM直接アクセスが必要になります。ブラウザが提供するAPIの中には、単純に状態からUIへのマッピングとして表現できないものがあります。

DOM直接アクセスの一般的なユースケース

フォーカス管理がリストのトップです。input要素で.focus()を呼び出すには、その要素への参照が必要です。どれだけ状態を操作しても、カーソルをテキストフィールドに移動させることはできません。

スクロールも同様の課題を提示します。特定の要素や位置にプログラム的にスクロールするには、命令的なDOM呼び出しが必要です。

レイアウト測定には直接アクセスが必要です。要素がDOMに存在するまで、その寸法や位置を知ることはできません。getBoundingClientRect()を読み取ったり、ResizeObserverIntersectionObserverと統合したりするには、実際のノード参照が必要です。

サードパーティライブラリの統合では、しばしばrefsが必要になります。D3、ビデオプレーヤー、canvasベースのツールなどのライブラリは、直接操作できるDOMノードを期待しています。

核となるトレードオフ

Refsは宣言的モデルを破ります。DOMノードを取得して直接操作する場合、フレームワークの知識の外で作業していることになります。これにより、コードとレンダリングされた構造の間に密結合が生じます。

Refsは控えめに使用してください。状態やpropsで処理できる問題を解決するためにrefに手を伸ばしている場合は、再考してください。Refsは主要なツールではなく、脱出ハッチであるべきです。

フレームワーク固有の実装

React Refs

React 19では、refsは通常のpropsとして関数コンポーネントに渡すことができます。forwardRefラッパーはほとんどのユースケースで必須ではなくなり、コンポーネントの合成が大幅に簡素化されました。

React 19では、コールバックrefsがクリーンアップ関数を返すことができ、要素がアンマウントされたときにイベントリスナーをデタッチしたり、クリーンアップを実行したりできます(古いrefsは後方互換性のために依然としてnullを受け取ります)。Strict Modeでは開発中にrefコールバックが複数回呼び出される可能性があることに注意してください—コードはこれを適切に処理する必要があります。

React refsはミュータブルなコンテナです。.currentを変更しても再レンダリングはトリガーされないため、更新サイクルを引き起こすことなくDOM参照を保存するのに理想的です。

Vue Template Refs

Vueはテンプレート内のref属性を通じてrefsを公開します。Composition APIでは、ref(null)でrefを作成し、マウント後に要素にアクセスします。

VueはdefineExpose()を通じてコンポーネント内部を明示的に公開することを推奨しています。これにより、実装の詳細への偶発的な結合を防ぎながら、必要に応じて制御されたアクセスを許可します。

Angular ElementRef

AngularはDOMアクセスのためにElementRefRenderer2を提供します。ドキュメントはこれらを明示的に最後の手段のツールとしてラベル付けしています。ElementRefを使用してもDOMアクセスが自動的に「安全」になるわけではありません—依然としてAngularの抽象化をバイパスしています。Renderer2は主にプラットフォームの抽象化(SSRなど)を支援するものであり、セキュリティのためではありません。

可能な限りAngularの組み込みディレクティブとバインディングを優先してください。宣言的な代替手段が存在しない場合にのみElementRefを使用してください。

Svelte bind:this

Sveltebind:thisを使用して要素参照をキャプチャします。バインディングはコンポーネントのマウント後に設定されるため、初期スクリプト実行中に要素にアクセスすることはできません。

SvelteでのDOMアクセスは、通常onMountまたは$effect(Svelte 5)を介して、マウント後のクライアント側でのみ発生します。サーバーサイドレンダリングはクライアント側のバインディングを実行せずにHTMLを生成するため、refsはハイドレーションが完了するまで未定義のままです。

SSRとハイドレーションのタイミング

すべてのフレームワークにおいて、refsはマウント後のクライアント側でのみ存在します。サーバーサイドレンダリング中はDOMは存在せず、HTML文字列のみです。コードはこれを考慮する必要があります。

ライフサイクルチェックでrefアクセスを保護してください。Reactではエフェクト内でrefsにアクセスします。VueではonMountedを使用します。SvelteではonMountまたはハイドレーション後に実行されるリアクティブステートメントを使用します。SSR中にrefsにアクセスしようとすると、未定義の値またはエラーが発生します。

Refsに手を伸ばすべきとき

自問してください:これは宣言的に解決できますか?もしイエスなら、refを避けてください。ブラウザAPIが本当にDOMノードを必要とする場合—フォーカス、スクロール、測定、または統合—その場合、refsが適切なツールです。

refの使用を隔離してください。命令的ロジックをカスタムフックまたはコンポーザブルでラップします。これにより結合が抑制され、コードを読む他の開発者に脱出ハッチが明示的になります。

結論

Refsは非推奨でも推奨されないものでもありません。それらはすべてのフレームワーク設計の意図的な部分です。意図的に使用し、トレードオフを理解すれば、ブラウザプラットフォームの全機能にアクセスしながら、アプリケーションの保守性を維持できます。

よくある質問

refsを使用して要素のスタイルやコンテンツを直接更新できますか?

技術的には可能ですが、避けるべきです。スタイルやコンテンツを直接操作すると、フレームワークのレンダリングサイクルをバイパスし、コンポーネントの状態と実際のDOMの間に不整合が生じる可能性があります。代わりに状態駆動のスタイリングとコンテンツ更新を使用してください。フォーカス、スクロール、測定など、本当にDOMノードを必要とする操作のためにrefsを確保してください。

なぜrefがnullまたはundefinedを返すのですか?

Refsはコンポーネントのマウント後にのみ設定されます。初期レンダリング中またはサーバーサイドレンダリング中にrefにアクセスすると、nullまたはundefinedになります。DOM要素が存在することを確認してから操作するために、Reactの useEffect、Vueの onMounted、Svelteの onMount などのライフサイクルフックで常にrefアクセスを保護してください。

親子コンポーネント間の通信にrefsを使用すべきですか?

一般的にはノーです。Refsはコンポーネント間に密結合を作り、標準的なデータフローをバイパスします。親子間の通信にはpropsとコールバックを優先してください。子要素でフォーカスやスクロールをトリガーするなど、DOM直接アクセスが必要な場合にのみrefsを使用してください。Vueでは、defineExposeを使用して子コンポーネントが公開する内容を制御します。

refsはすべてのフロントエンドフレームワークで同じように動作しますか?

概念は似ていますが、実装は異なります。Reactはcurrentプロパティを持つミュータブルなrefオブジェクトを使用します。VueはComposition API経由でアクセスされるtemplate refsを使用します。AngularはElementRefとRenderer2を提供します。Svelteはbind:thisディレクティブを使用します。各アプローチはフレームワークのアーキテクチャを反映しているため、正しい使用方法についてはフレームワーク固有のドキュメントを参照してください。

DevTools for the frontend

Gain Debugging Superpowers

Unleash the power of session replay to reproduce bugs, track slowdowns and uncover frustrations in your app. Get complete visibility into your frontend with OpenReplay — the most advanced open-source session replay tool for developers.

Star on GitHub12k

We use cookies to improve your experience. By using our site, you accept cookies.