Reactにおいてフックとは、開発者がクラスコンポーネントを必要とせずにReactのstateやその他の機能を使用できるようにする特別な機能です。フックの中でもuseRefフックは、値を管理したり、DOM(Document Object Model)要素にアクセスしたりするための貴重な機能として際立っています。

useRefフックは計り知れない柔軟性と可能性をもたらす強力なものですが、多くの開発者がこれの使い方を誤解しています。

この記事では、useRefフックを深掘りし、その目的、機能、ベストプラクティスを明らかにしたいと思います。この記事を読み終える頃には、フックの本質を理解し、その可能性を効果的に活用する方法について洞察を獲得できているはずです。

useRefフックとは

useRefフックの主な目的は、更新されても再レンダリングが発生しないような(変更可能な)値を保存することと、DOM要素への参照を保存することの2つです。このフックがどのように動作するのか、もう少し詳しく見てみましょう。

Reactでコンポーネントがレンダリングされると、その状態(state)やその他の変数は通常リセットされます。しかし、コンポーネントを再レンダリングしても特定の値を保持する必要がある場合があります。そのような状況で、useRefフックが活躍します。これにより、レンダリング間で永続するかたちで値を参照し、コンポーネントの他の部分が変更されても値がそのまま残るようにすることができます。

さらに、useRefフックは、DOM要素の操作にも有用です。Reactでは、DOM要素に直接アクセスしたり変更したりすることは、特にuseRefフックなしでは困難です。useRefを使えば、特定のDOM要素への参照を取得し、その要素に対して操作を行うことができます。これにより、外部ライブラリや複雑な回避策が不要になります。

ReactでuseRefを実装する

ReactプロジェクトでuseRefフックを使い始めるには、Reactパッケージからインポートします。

import { useRef } from 'react';

インポートしたら、useRefフックを使用して、関数コンポーネント内でref変数を宣言します。

const myRef = useRef();

これで、myRefというrefオブジェクトが作成され、値の格納やアクセスに使用できるようになります。どの要素でもmyRef変数を使用するには、その要素のref propに代入します。

<div ref={myRef}>これは要素の例です</div>

上の例では、div要素にref propを代入しています。これにより、コンポーネント内の他の場所でmyRef変数を使用して要素を参照し、アクセスすることができます。

この参照に格納された値にアクセスするには、myRefオブジェクトの.currentプロパティを使用します。

const myRefValue = myRef.current;
console.log(myRefValue); // <div>これはサンプルのdivです</div>

useRefフックによるDOM操作

DOMの操作はウェブ開発では一般的な操作です。これによりウェブページの内容や構造、外観を動的に変更したり更新したりすることができます。

従来のJavaScript開発では、DOM要素にアクセスして操作するには、getElementByIdquerySelectorgetElementsByClassNameのようなメソッドを使用して、ドキュメントから特定の要素を選択する必要がありました。一度選択すると、コンテンツを更新したり、スタイルを変更したり、イベントリスナーを紐付けたりすることができます。

// HTML
<div>
  <input type="text" id="myInput" />
  <button id="focusButton">クリックでフォーカス</button>
</div>
// JavaScript
<script>
      const inputRef = document.getElementById('myInput');
      const focusButton = document.getElementById('focusButton');
      const handleFocus = function() {
        inputRef.focus();
      };
      focusButton.addEventListener('click', handleFocus);
</script>

しかし、ReactコンポーネントでDOM要素を扱う場合、コンポーネントの仮想DOMという性質から、そして効率的に更新を行う必要性のため、実際のプロセスは異なります。開発者はしばしば、DOM要素にアクセスし操作するために、参照やjQueryのような外部ライブラリを使用するなど、さまざまなアプローチに頼ることが一般的でした。

そしてReactにuseRefフックが導入されたことで、コンポーネント内でDOM要素を扱うプロセスが大幅に合理化されています。useRefフックにより、DOM要素への参照を簡単に作成し、コンポーネントのコンテキスト内で簡単にアクセスして操作できるようになります。

import { useRef } from 'react';

const FocusComponent = () => {
  const inputRef = useRef(null);

  const handleFocus = () => {
    // 入力要素へのアクセス
    let inputElement = inputRef.current;

   // DOM要素を変更
   inputElement.focus();
  };
 
 return (
    <div>
      <input type="text" ref={inputRef} />
      <button onClick={handleFocus}>Focus Input</button>
    </div>
  );
}

この例では、useRefフックを使用して、input要素を指す参照としてinputRefを作成しています。「クリックでフォーカス」ボタンをクリックすると、handleFocus関数がinputRef.current.focus()を利用して、入力要素にフォーカスを設定します。これは、useRefフックが、ReactでDOM要素を扱うプロセスをいかに単純化するかを示す良い例でしょう。

別の例として、ボタンがクリックされたときに背景を変更するというdivの操作を行いたいとします。

import { useRef } from 'react';

const ExampleComponent = () => {
  const divRef = useRef();

  const handleClick = () => {
    divRef.current.style.backgroundColor = 'red';
  };

  return (
    <div>
      <div ref={divRef}>これはサンプルのdivです</div>
      <button onClick={handleClick}>色を変更する</button>
    </div>
  );
}

この例では、useRefフックでdivRefという参照を作成します。この参照をdiv要素のref propに割り当てます。

「色を変える」ボタンをクリックすると、handleClick関数が呼び出されます。この関数では、divRef.currentdiv要素にアクセスできます。この場合、style.backgroundColorプロパティを「red」に変更して、div要素の背景色を変更します。

divRef.current.style.backgroundColor = 'red';

再レンダリング時の値の保持

再レンダリングをまたいだ値の保持は、useRefフックの便利な使用例です。再レンダリングをトリガーすることなく、コンポーネントのライフサイクルを通して値を保持する必要がある場合に有益です。

この概念をよく理解するために、実際の例を使ってuseRefフックとuseStateフックを比較してみましょう。

useStateフックを使った例は以下の通りです。

import { useState } from 'react';

function CounterComponent() {
  const [count, setCount] = useState(0);

  const increment = () => {
    setCount(count + 1);
  };

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={increment}>Increment</button>
    </div>
  );
}

この例では、useStateフックを使って、countというstate変数を管理しています。increment関数が呼ばれるたびに、setCountを使ってcountの状態が更新されます。これにより、コンポーネントの再レンダリングがトリガーされ、更新後のcountの値が反映されます。

Page re-renders with the useState Hook

useRefフックを使った例は以下の通りです。

import React, { useRef } from 'react';

function CounterComponent() {
  const countRef = useRef(0);

  const increment = () => {
    countRef.current = countRef.current + 1;
    console.log('Count:', countRef.current);
  };

  return (
    <div>
      <p>Count: {countRef.current}</p>
      <button onClick={increment}>Increment</button>
    </div>
  );
}

この例では、useRefフックを使って、初期値0で初期化された変数countRefを作成しています。increment関数が呼ばれるたびに、再レンダリングをトリガーすることなく、countRef.currentの値を直接変更します。変更した後の値はコンソールに記録されます。

Page does not re-renders with the useRef Hook

useRefフックを使用すると、countRef.currentの値が変更されてもコンポーネントの再レンダリングは行われません。これは、値を変更する必要があるもののレンダリング処理には影響を及ぼしたくないという状況で効果を発揮します。

この方法でuseRefフックを使用すると、ref値の変更は自動で再レンダリングをトリガーしませんのでこの点にはご注意ください。更新した値をUIに反映させる必要がある場合は、手動で変更を処理するか、useRefフックを他のフックやstate変数と組み合わせることができます。

まとめ

この記事では、ReactのuseRefフックについて説明し、その目的、実装、実際の用途を扱いました。またDOM要素へのアクセスや変更、値の保持にuseRefを使用する方法をご紹介しました。

useRefフックのベストプラクティスは、使いすぎないことです。DOM要素へのアクセスや操作、再レンダリング時の値の保持が特に必要な場合に使用するようにしてください。

useRefフックには、アニメーションやトランジション、値や中間結果のキャッシュなど、Reactアプリケーションを際立たせるさまざまな実用的な用途があります。

次のReactアプリケーション構築の際には、Kinstaのアプリケーションホスティングを無料でデプロイにお試しください。

あなたはuseRefフックについてどう思いますか?以下のコメント欄でお聞かせください。