はじめに
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の基本、useStateを理解しよう
-
Reactの基本、useEffectを理解しよう
-
Reactの基本、useContextを理解しよう
-
Reactの基本、useReducerを理解しよう
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;
-
useRefのインポート
はじめにコンポーネントのトップレベルでuseRefをインポートします。(コード例ではuseStateも一緒にインポートしています)
import { useRef } from 'react';
-
refの作成
intervalRef
というrefを作成し、setInterval
で生成されたインターバルIDを保持します。これにより、再レンダリングが発生してもインターバルIDがリセットされることなく保持されます。初期値はnull
を定義しています。
const intervalRef = useRef(null);
-
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);
};
- stopTimer関数
タイマーが動いている場合、clearInterval
を使ってタイマーを停止し、intervalRef.current
をnull
にリセットします。
//タイマーを停止
const stopTimer = () => {
// インターバルが設定されている場合のみ停止処理を行う
if (intervalRef.current !== null) {
// インターバルをクリアしてタイマーを停止
clearInterval(intervalRef.current);
// インターバルIDをリセットして無効化
intervalRef.current = null;
}
このようなコードの流れで、タイマーの開始と停止を制御します。refにタイマーのインターバルIDを保持し、そのIDをReactが参照することで、進み続けるタイマーを後から停止することができます。
useRefを使用してみよう
ここまで、useRefの基本について解説してきました。次に、実際にコードを試して、useRefの理解を深めていきましょう。このハンズオンでは、ページ内の目次リストをクリックすると、そのセクションに自動スクロールする機能を作成します。
ハンズオンを手軽に試したい方には、以下のサイトがおすすめです。Reactの環境構築が不要で、ブラウザ上でコードをすぐに試すことができます。
-
StackBlitz
-
StackBlitzについては、以下のサイトで詳しく解説しています。
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つずつ確認していきましょう。
-
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要素を指すようになります。 -
スクロールを制御する関数
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'
で要素がページの上部に表示されるように指定しています。 -
目次とスクロール
<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を学び始める全ての人に知って欲しい。そう思って書きました。是非読んでみてください。
「フリーランスエンジニア」
近年やっと世間に浸透した言葉だ。ひと昔まえ、終身雇用は当たり前で、大企業に就職することは一種のステータスだった。しかし、そんな時代も終わり「優秀な人材は転職する」ことが当たり前の時代となる。フリーランスエンジニアに高価値が付く現在、ネットを見ると「未経験でも年収400万以上」などと書いてある。これに釣られて、多くの人がフリーランスになろうとITの世界に入ってきている。私もその中の1人だ。数年前、USBも知らない状態からITの世界に没入し、そこから約2年間、毎日勉学を行なった。他人の何十倍も努力した。そして、企業研修やIT塾で数多くの受講生の指導経験も得た。そこで私は、伸びるエンジニアとそうでないエンジニアをたくさん見てきた。そして、稼げるエンジニア、稼げないエンジニアを見てきた。
「成功する人とそうでない人の違いは何か?」
私が出した答えは、「量産型エンジニアか否か」である。今のエンジニア市場には、量産型エンジニアが溢れている!!ここでの量産型エンジニアの定義は以下の通りである。
比較的簡単に学習可能なWebフレームワーク(WordPress, Rails)やPython等の知識はあるが、ITの基本概念を理解していないため、単調な作業しかこなすことができないエンジニアのこと。
多くの人がフリーランスエンジニアを目指す時代に中途半端な知識や技術力でこの世界に飛び込むと返って過酷な労働条件で働くことになる。そこで、エンジニアを目指すあなたがどう学習していくべきかを私の経験を交えて書こうと思った。続きはこちらから、、、、
エンベーダー編集部
エンベーダーは、ITスクールRareTECHのインフラ学習教材として誕生しました。 「遊びながらインフラエンジニアへ」をコンセプトに、インフラへの学習ハードルを下げるツールとして運営されています。
関連記事
2020.02.25
完全未経験からエンジニアを目指す爆速勉強法
USBも知らなかった私が独学でプログラミングを勉強してGAFAに入社するまでの話
- キャリア・学習法
- エンジニア
2024.10.31
Reactの基本、カスタムフックを理解しよう
この記事では、初学者の方が理解しやすいように、シンプルなコード例とともにカスタムフックについて詳しく解説します。
- React
- フロントエンド
2024.10.28
Reactの基本、useCallbackを理解しよう
useCallbackは、関数をメモ化し、再レンダリング時に不必要な関数の再作成を避けることができる仕組みです。
- React
- フロントエンド
2024.08.01
Reactの基本、useStateを理解しよう
フロントエンドエンジニアを目指す方や、初めてReactを学ぶ方に向けて、この記事ではReact Hooksの中でも使用頻度の高いuseStateについて解説します。コンポーネントの状態管理は、アプリケーションを構成する上で必須の知識です。useStateの基本を押さえておくことで、これからのReact学習の理解度が深まります。基本を学び、フロントエンドエンジニアへの道を進んでいきましょう!
- React
- フロントエンド