1. ホーム
  2. 記事一覧
  3. Reactの基本、useRefを理解しよう

2024.08.31

Reactの基本、useRefを理解しよう

はじめに

Reactは、ユーザーインターフェースを構築するためのJavaScriptライブラリです。近年では、ウェブアプリケーションやモバイルアプリケーションのインターフェースを作成するために、多くの開発現場で使用されています。Reactを学ぶことで、モダンなウェブ開発のスキルを身につけることができるため、フロントエンドエンジニアを目指す上で必須のスキルと言っても過言ではありません。

Reactを学んでいる方の中で、React Hooksについて理解が進まない、どの場面で使うのかよく分からない、と感じる方もいるのではないでしょうか?そんな方々に向けて、React Hooksを紹介するシリーズをお届けします。今回は、DOM要素に直接アクセスできるuseRefについて解説します。この記事を通して、 useRefの基本知識と使い方を学ぶことができます。 基本をしっかりと押さえることで、Reactの理解が深まり、さらにステップアップが期待できるでしょう。一緒に学び、フロントエンドエンジニアとしての第一歩を踏み出しましょう!

この記事について

目的

  • React useRefについて知る
  • React useRefを使えるようにする

対象者

  • 初学者の方
  • Reactの学習をしている方

Reactの基本について

Reactの基本の説明はこの記事では省略しています。基本や環境構築方法などを知りたい方は、下記のページをご覧ください。

https://envader.plus/article/341

React Hooksシリーズ

以下はReact Hooksシリーズの記事一覧です。この記事とあわせて読むことで、より理解が深まりますのでぜひご覧ください。

React useRefについて

useRefはReact Hooksの1つで、コンポーネント内でref(参照)を保持するために使用されます。useRefが役立つ場面としては主に2つあります。

1つ目は、コンポーネントの値を保持し、コンポーネントが再レンダリングしてもその値を保持したい場合です。通常、Reactコンポーネントは再レンダリングされると、変数の値はリセットされますが、useRefを使用すると、レンダリング間で値を保持し続けることができます。

2つ目は、DOM要素への直接的な参照が必要な場合です。useRefでDOM要素を参照することで、フォームが表示されたときに自動で入力欄にフォーカスしたり、ページ内の特定のDOM要素へ自動スクロールするなどの操作が可能になります。

refのDOM操作について

Reactでは、コンポーネントの状態(state)やプロパティ(props)を使用してUIを表現しています。仮想DOMを通じてDOM要素の更新を行うため、直接DOM要素へアクセスして操作することは推奨されていません。しかし、特定の場面でDOM要素に直接アクセスしたり操作する必要がある場合には、ref を利用することが許容されることがあります。ただし、このような操作は慎重に行う必要があります。

関数コンポーネントでは useRefを使うことでrefの効果を利用できますが、DOM操作は最小限に抑えることが推奨されます。Reactのコンポーネントは、「状態管理によるUIの一貫した制御」を基本としています。そのため、useRefによるDOM操作は特別なケースに限定し、一般的にはstateや propsを使ってUIを制御することが望ましいでしょう。

Reactにおけるstateとrefの違い

コンポーネントの状態を保持するために、stateを思い浮かべる方も多いかと思います。Reactにおけるstateとrefは、どちらもコンポーネント内でデータを保持するために使われますが、それぞれ異なる役割と使い方があります。

  • state

    stateは、Reactコンポーネント内で管理されるデータを指します。stateに変更があると、Reactはその変更を検知し、コンポーネントを再レンダリングします。

  • ref

    refはstateとは異なり、再レンダリングに影響を与えないデータを保持するために使います。

stateはコンポーネントが「どう見えるか」を決めるデータ(例:カウントの値や入力フォームの内容)を管理します。一方、refは直接UIには表示されないが、コンポーネントの機能を支えるデータ(例:DOM要素へのアクセスやインターバルIDなど)を管理します。両方ともReactアプリケーションで非常に重要ですが、それぞれ異なる場面で使い分けられます。

たとえば、タイマーアプリを例にすると、stateはカウントアップする数値を保持し、その値の変更が発生するたびに再レンダリングをトリガーします。一方で、refはタイマーのインターバルIDを保持します。refがこのインターバルIDを持つことで、タイマーのカウントアップ処理を後で停止することが可能になります。

useRefの使い方

ここまでuseRefの基本と、stateとの違いを学んできました。次に、useRefを使用したタイマーアプリのコード例を見ながら、理解を深めていきましょう。

以下のコードは、タイマー機能をuseRefを使用して記述したものです。

import { useState, useRef } from 'react';

function App() {
  const [seconds, setSeconds] = useState(0);
  // インターバルIDを保持するためのref
  const intervalRef = useRef(null);
	
	// タイマーを開始
  const startTimer = () => {
	  // タイマーがすでに動いている場合は何もしない
    if (intervalRef.current !== null) return;
		// 1秒ごとにsecondsの値を増やすインターバルを設定
    intervalRef.current = setInterval(() => {
      setSeconds((prevSeconds) => prevSeconds + 1);
    }, 1000);
  };
  
	//タイマーを停止
  const stopTimer = () => {
	  // インターバルが設定されている場合のみ停止処理を行う
    if (intervalRef.current !== null) {
	    // インターバルをクリアしてタイマーを停止
      clearInterval(intervalRef.current);
      // インターバルIDをリセットして無効化
      intervalRef.current = null;
    }
  };

  return (
    <div>
      <h1>Timer: {seconds} seconds</h1>
      <button onClick={startTimer}>Start</button>
      <button onClick={stopTimer}>Stop</button>
    </div>
  );
}

export default App;
  1. useRefのインポート

    はじめにコンポーネントのトップレベルでuseRefをインポートします。(コード例ではuseStateも一緒にインポートしています)

import { useRef } from 'react';
  1. refの作成

    intervalRefというrefを作成し、setIntervalで生成されたインターバルIDを保持します。これにより、再レンダリングが発生してもインターバルIDがリセットされることなく保持されます。初期値はnullを定義しています。

const intervalRef = useRef(null);
  1. startTimer関数

    タイマーを開始します、すでにタイマーが開始されている場合は何もしません。setSeconds((prevSeconds) => prevSeconds + 1); }, 1000);の部分は、現在の秒数(prevSeconds)にprevSeconds + 1で1秒ずつ値を増やします。setIntervalの第2引数に、1000ミリ秒(1秒)が指定されており、1秒ごとにこの関数が繰り返し実行されます。インターバルIDはintervalRef.currentに保存されます。

// タイマーを開始
const startTimer = () => {
  // タイマーがすでに動いている場合は何もしない
  if (intervalRef.current !== null) return;
	// 1秒ごとにsecondsの値を増やすインターバルを設定
  intervalRef.current = setInterval(() => {
    setSeconds((prevSeconds) => prevSeconds + 1);
  }, 1000);
};
  1. stopTimer関数

タイマーが動いている場合、clearIntervalを使ってタイマーを停止し、intervalRef.currentnullにリセットします。

//タイマーを停止
const stopTimer = () => {
// インターバルが設定されている場合のみ停止処理を行う
if (intervalRef.current !== null) {
  // インターバルをクリアしてタイマーを停止
  clearInterval(intervalRef.current);
  // インターバルIDをリセットして無効化
  intervalRef.current = null;
}

このようなコードの流れで、タイマーの開始と停止を制御します。refにタイマーのインターバルIDを保持し、そのIDをReactが参照することで、進み続けるタイマーを後から停止することができます。

useRefを使用してみよう

ここまで、useRefの基本について解説してきました。次に、実際にコードを試して、useRefの理解を深めていきましょう。このハンズオンでは、ページ内の目次リストをクリックすると、そのセクションに自動スクロールする機能を作成します。

ハンズオンを手軽に試したい方には、以下のサイトがおすすめです。Reactの環境構築が不要で、ブラウザ上でコードをすぐに試すことができます。

useRefを使用した自動スクロール機能の作成

このハンズオンで作成する自動スクロール機能では、useRefフックを使ってセクションのh1要素をrefの値として取得し、その要素へ画面をスクロールする機能です。

App.jsxを以下のように編集します。

// App.jsx

import { useRef } from 'react';

function App() {
  // 各セクションのh1要素を参照するためのrefを作成
  const section1Ref = useRef(null);
  const section2Ref = useRef(null);
  const section3Ref = useRef(null);

  // スクロールするための関数
  const scrollToSection = (sectionRef) => {
    // 参照しているセクションにスクロール
    if (sectionRef.current) {
      sectionRef.current.scrollIntoView({
        behavior: 'smooth',
        block: 'start', // セクションの上部がビューのトップに位置するようにスクロール
      });
    }
  };

  // スタイルオブジェクトを定義
  const sectionStyle = {
    height: '120vh',
    width: '80vh',
    marginTop: '20vh',
  };

  const liStyle = {
    cursor: 'pointer',
    margin: '5px 0px',
    listStyleType: 'none',
  };

  return (
    <>
      {/* 目次 */}
      <nav>
        <ul style={{ padding: '0px' }}>
          <li style={liStyle} onClick={() => scrollToSection(section1Ref)}>
            目次1: セクション1へスクロール
          </li>
          <li style={liStyle} onClick={() => scrollToSection(section2Ref)}>
            目次2: セクション2へスクロール
          </li>
          <li style={liStyle} onClick={() => scrollToSection(section3Ref)}>
            目次3: セクション3へスクロール
          </li>
        </ul>
      </nav>

      {/* セクション1 */}
      <section style={{ ...sectionStyle }}>
        <h1 ref={section1Ref}>セクション1</h1>
        <div>
          <p>エンベーダーでインフラの勉強をしよう!</p>
        </div>
      </section>

      {/* セクション2 */}
      <section style={{ ...sectionStyle }}>
        <h1 ref={section2Ref}>セクション2</h1>
        <div>
          <p>エンベーダーでは、Linuxのコードをゲーム感覚で学習できる!</p>
        </div>
      </section>

      {/* セクション3 */}
      <section style={{ ...sectionStyle }}>
        <h1 ref={section3Ref}>セクション3</h1>
        <div>
          <p>エンベーダーで学習して、一人前のインフラエンジニアへ!</p>
        </div>
      </section>
    </>
  );
}

export default App;

コードを編集後、以下のようなUIが作成できます。目次をそれぞれクリックし、各セクションへ自動スクロールすることを確認しましょう。

コードの解説

ここで、今回作成したコードのuseRefに関する部分を1つずつ確認していきましょう。

  1. useRefの定義

    function App() {
      // 各セクションのh1要素を参照するためのrefを作成
      const section1Ref = useRef(null);
      const section2Ref = useRef(null);
      const section3Ref = useRef(null);
    <h1 ref={section1Ref}>セクション1</h1>
    <h1 ref={section2Ref}>セクション2</h1>
    <h1 ref={section3Ref}>セクション3</h1>

    section1Ref, section2Ref, section3Refという3つのrefを作成しており、各refは、対応するセクションのh1要素を参照するために使います。初期値としてnullを渡していますが、実際に要素がrefに設定されるのは、コンポーネントがレンダリングされた後です。

    ※なぜnullが初期値なのか

    Reactコンポーネントが初めてレンダリングされるとき、DOM要素はまだ存在していません。そのため、refにはまだ何も参照する対象がない状態です。このため、初期値としてnullを設定しておくのが一般的です。

    コンポーネントがレンダリングされた後、Reactはrefを対応するDOM要素に自動的に紐付けます。これにより、ref.currentが有効なDOM要素を指すようになります。

  2. スクロールを制御する関数

    const scrollToSection = (sectionRef) => {
      if (sectionRef.current) {
        sectionRef.current.scrollIntoView({
          behavior: 'smooth',
          block: 'start', // セクションの上部がビューのトップに位置するようにスクロール
        });
      }
    };

    scrollToSection関数は、指定されたrefを使って、その参照先の要素にスクロールします。目次のli要素をクリックした際に、そのli要素に対応するセクションのrefがsectionRef.currentに引数として渡されており、scrollIntoViewメソッドを使ってそのh1要素へ画面がスクロール表示されます。

    scrollIntoViewメソッドは、その要素が画面内のどこに表示されるかを制御します。ここでは、behavior: 'smooth'でスムーズなスクロールを実現し、block: 'start'で要素がページの上部に表示されるように指定しています。

  3. 目次とスクロール

    <li style={liStyle} onClick={() => scrollToSection(section1Ref)}>
      目次1: セクション1へスクロール
    </li>
    <li style={liStyle} onClick={() => scrollToSection(section2Ref)}>
      目次2: セクション2へスクロール
    </li>
    <li style={liStyle} onClick={() => scrollToSection(section3Ref)}>
      目次3: セクション3へスクロール
    </li>

    目次の各li要素をクリックすると、それぞれ対応するセクションにスクロールします。クリック時にscrollToSection関数が呼ばれ、その引数に該当するrefが渡されます。これにより、refが指すh1要素にスクロールされます。

このハンズオンでは、useRefを使って各セクションのh1要素にアクセスし、その要素にスクロールする機能を実装しました。useRefを使うことで、特定のDOM要素を参照でき、それにもとづいてユーザーの操作に反応することができます。

コードをカスタムして理解を深めよう

ハンズオンのコードを理解したあとは、ご自身でuseRefを使用した機能を追加してみましょう。例えば、セクション3に「目次へ戻る」ボタンを追加し、そのボタンをクリックするとページの目次へスクロールして戻る機能を実装することが考えられます。このように、自分で新しい機能を考えて実装してみることで、useRef の理解がさらに深まるでしょう。ぜひ挑戦してみてください!

この記事で学んだこと

ここまでuseRefについて、その基本と実際の使い方を学びました。簡単に振り返ってみましょう。

  • React useRefについて

    useRefはReact Hooksの1つで、コンポーネント内でref(参照)を保持するために使用されます。コンポーネントの値を保持し、コンポーネントが再レンダリングしてもその値を保持したい場合や、DOM要素への直接的な参照が必要な場合に、useRefの機能を利用し値を保持します。

  • Reactにおけるstateとrefの違い

    Reactにおけるstateとrefは、どちらもコンポーネント内でデータを保持するために使われますが、それぞれ異なる役割と使い方があります。

    • state

      stateは、Reactコンポーネント内で管理されるデータを指します。stateに変更があると、Reactはその変更を検知し、コンポーネントを再レンダリングします。

    • ref

      refはstateとは異なり、再レンダリングに影響を与えないデータを保持するために使います。

    stateはコンポーネントが「どう見えるか」を決めるデータを管理し、refは直接UIには表示されないが、コンポーネントの機能を支えるデータを管理します。両方ともReactアプリケーションで非常に重要ですが、それぞれ異なる場面で使い分けられます。

参考資料

以下のリンクは、この記事で説明した手順や概念に関連する参考資料です。より詳しく学びたい方は、ぜひご参照ください。

【番外編】USBも知らなかった私が独学でプログラミングを勉強してGAFAに入社するまでの話

IT未経験者必見 USBも知らなかった私が独学でプログラミングを勉強してGAFAに入社するまでの話

プログラミング塾に半年通えば、一人前になれると思っているあなた。それ、勘違いですよ。「なぜ間違いなの?」「正しい勉強法とは何なの?」ITを学び始める全ての人に知って欲しい。そう思って書きました。是非読んでみてください。

「フリーランスエンジニア」

近年やっと世間に浸透した言葉だ。ひと昔まえ、終身雇用は当たり前で、大企業に就職することは一種のステータスだった。しかし、そんな時代も終わり「優秀な人材は転職する」ことが当たり前の時代となる。フリーランスエンジニアに高価値が付く現在、ネットを見ると「未経験でも年収400万以上」などと書いてある。これに釣られて、多くの人がフリーランスになろうとITの世界に入ってきている。私もその中の1人だ。数年前、USBも知らない状態からITの世界に没入し、そこから約2年間、毎日勉学を行なった。他人の何十倍も努力した。そして、企業研修やIT塾で数多くの受講生の指導経験も得た。そこで私は、伸びるエンジニアとそうでないエンジニアをたくさん見てきた。そして、稼げるエンジニア、稼げないエンジニアを見てきた。

「成功する人とそうでない人の違いは何か?」

私が出した答えは、「量産型エンジニアか否か」である。今のエンジニア市場には、量産型エンジニアが溢れている!!ここでの量産型エンジニアの定義は以下の通りである。

比較的簡単に学習可能なWebフレームワーク(WordPress, Rails)やPython等の知識はあるが、ITの基本概念を理解していないため、単調な作業しかこなすことができないエンジニアのこと。

多くの人がフリーランスエンジニアを目指す時代に中途半端な知識や技術力でこの世界に飛び込むと返って過酷な労働条件で働くことになる。そこで、エンジニアを目指すあなたがどう学習していくべきかを私の経験を交えて書こうと思った。続きはこちらから、、、、

note記事3000いいね超えの殿堂記事 今すぐ読む

エンベーダー編集部

エンベーダーは、ITスクールRareTECHのインフラ学習教材として誕生しました。 「遊びながらインフラエンジニアへ」をコンセプトに、インフラへの学習ハードルを下げるツールとして運営されています。

RareTECH 無料体験授業開催中! オンラインにて実施中! Top10%のエンジニアになる秘訣を伝授します! RareTECH講師への質疑応答可

関連記事