v19.2Latest

RefによるDOMの操作

Reactはレンダー出力に合わせて自動的にDOMを更新するため、コンポーネントがDOMを操作する必要はあまりありません。しかし、Reactによって管理されているDOM要素にアクセスする必要がある場合があります。例えば、ノードにフォーカスを当てたり、スクロールさせたり、サイズや位置を測定したりする場合です。Reactにはこれらのことを行う組み込みの方法がないため、DOMノードへのrefが必要になります。

学習内容
  • Reactによって管理されているDOMノードにref属性でアクセスする方法
  • JSXのref属性とuseRefフックの関係
  • 他のコンポーネントのDOMノードにアクセスする方法
  • Reactによって管理されているDOMを安全に変更できる場合

ノードへのrefの取得

Reactによって管理されているDOMノードにアクセスするには、まずuseRefフックをインポートします:

次に、コンポーネント内でrefを宣言するために使用します:

最後に、DOMノードを取得したいJSXタグのref属性としてrefを渡します:

useRefフックは、current初期状態では、myRef.currentnullになります。Reactがこの<div>のDOMノードを作成すると、Reactはそのノードへの参照をmyRef.currentに格納します。その後、イベントハンドラからこのDOMノードにアクセスし、その上で定義されている組み込みのブラウザAPIを使用できます。

例: テキスト入力へのフォーカス

この例では、ボタンをクリックすると入力欄にフォーカスが当たります:

これを実装するには:

  1. inputRefuseRefフックで宣言します。
  2. それを<input ref={inputRef}>として渡します。これにより、Reactはこの <input>のDOMノードをinputRef.currentに格納します。
  3. handleClick 関数内で、 inputRef.currentから入力DOMノードを読み取り、focus()inputRef.current.focus()で呼び出します。
  4. handleClickイベントハンドラを<button>onClickに渡します。

DOM操作はrefの最も一般的なユースケースですが、useRefフックは、タイマーIDなど、Reactの外部に他のものを保存するためにも使用できます。stateと同様に、refはレンダー間で保持されます。refは、設定しても再レンダーをトリガーしないstate変数のようなものです。詳細はrefによる値の参照 をお読みください。

例: 要素へのスクロール

コンポーネント内に複数のrefを持つことができます。この例では、3つの画像からなるカルーセルがあります。各ボタンは、対応するDOMノードに対してブラウザのscrollIntoView()メソッドを呼び出すことで画像を中央に配置します:

他のコンポーネントのDOMノードへのアクセス

落とし穴

Refは緊急避難口です。他のコンポーネントのDOMノードを手動で操作すると、コードが脆弱になる可能性があります。

親コンポーネントから子コンポーネントへrefを渡すことができます。他のプロパティと同様に

上記の例では、親コンポーネントMyFormでrefが作成され、子コンポーネントMyInputに渡されます。MyInputはそのrefを<input>に渡します。<input>組み込みコンポーネントであるため、Reactはrefの.currentプロパティを<input>DOM要素に設定します。

これで、inputRefMyFormで作成され、<input>DOM要素を指すようになりました。これはMyInputによって返されます。MyFormで作成されたクリックハンドラはinputRefにアクセスし、focus()を呼び出して<input>にフォーカスを設定できます。

Reactがrefをアタッチするタイミング

Reactでは、すべての更新は2つのフェーズに分かれています:

  • レンダリングフェーズでは、Reactはコンポーネントを呼び出し、画面に何を表示すべきかを決定します。
  • コミットフェーズでは、ReactはDOMに変更を適用します。

一般的に、レンダリング中にrefにアクセスすることは望ましくありません。これはDOMノードを保持するrefにも当てはまります。最初のレンダリング時にはDOMノードはまだ作成されていないため、ref.currentnullになります。また、更新のレンダリング中は、DOMノードはまだ更新されていません。そのため、それらを読み取るには早すぎます。

Reactはコミットフェーズ中にref.currentを設定します。DOMを更新する前に、Reactは影響を受けるref.currentの値をnullに設定します。DOMを更新した後、Reactは直ちにそれらを対応するDOMノードに設定します。

通常、refにはイベントハンドラからアクセスします。refに対して何かを行いたいが、それを行う特定のイベントがない場合、Effectが必要になるかもしれません。Effectについては次のページで説明します。

Refsを使ったDOM操作のベストプラクティス

Refは緊急避難口です。「Reactの外に出る」必要がある場合にのみ使用すべきです。一般的な例としては、フォーカス管理、スクロール位置の管理、またはReactが公開していないブラウザAPIの呼び出しなどがあります。

フォーカスやスクロールのような非破壊的な操作に留めている限り、問題に遭遇することはないでしょう。しかし、DOMを手動で変更しようとすると、Reactが行っている変更と衝突するリスクがあります。

この問題を説明するために、この例には歓迎メッセージと2つのボタンが含まれています。最初のボタンは、Reactで通常行うように、条件付きレンダリングstateを使って表示を切り替えます。2番目のボタンは、remove() DOM APIを使用して、Reactの制御外で強制的にDOMから削除します。

「Toggle with setState」を数回押してみてください。メッセージは消えて再び表示されるはずです。次に「Remove from the DOM」を押してください。これにより強制的に削除されます。最後に、「Toggle with setState」を押してください:

DOM要素を手動で削除した後、setStateを使って再び表示しようとすると、クラッシュが発生します。これはDOMを変更してしまい、Reactがそれを正しく管理し続ける方法を知らないためです。

Reactによって管理されているDOMノードを変更しないでください。Reactによって管理されている要素を変更したり、子要素を追加・削除したりすると、上記のような視覚的な矛盾やクラッシュを引き起こす可能性があります。

しかし、これは全くできないという意味ではありません。注意が必要です。Reactが更新する理由がないDOMの一部は安全に変更できます。例えば、JSX内で常に空の<div>がある場合、Reactはその子リストに触れる理由がありません。したがって、そこに手動で要素を追加または削除することは安全です。

まとめ

  • Refは一般的な概念ですが、ほとんどの場合、DOM要素を保持するために使用されます。
  • ReactにDOMノードをmyRef.currentに配置するよう指示するには、<div ref={myRef}>を渡します。
  • 通常、Refはフォーカス、スクロール、DOM要素の測定といった非破壊的なアクションに使用されます。
  • コンポーネントはデフォルトではそのDOMノードを公開しません。refプロップを使用することで、DOMノードを公開することを選択できます。
  • Reactによって管理されているDOMノードを変更することは避けてください。
  • Reactによって管理されているDOMノードを変更する場合は、Reactが更新する理由のない部分を変更してください。

Try out some challenges

Challenge 1 of 4:Play and pause the video #

In this example, the button toggles a state variable to switch between a playing and a paused state. However, in order to actually play or pause the video, toggling state is not enough. You also need to call play() and pause() on the DOM element for the <video>. Add a ref to it, and make the button work.

For an extra challenge, keep the “Play” button in sync with whether the video is playing even if the user right-clicks the video and plays it using the built-in browser media controls. You might want to listen to onPlay and onPause on the video to do that.


Ref による DOM 操作 | React Learn - Reflow Hub