Reace Hooks その⑥

Next.js-2.React Hooks編-

useRefとは?

useRef は React の フック(Hook) の一つで、次のような用途で使われます。

① コンポーネントが再レンダリングされても値を保持するstate とは違い、変更しても再レンダリングしない)
② DOM(HTML要素)を直接操作するdocument.querySelector() のような処理を React で実装)

useRefの使い方

実際にコードを書いて見ていきましょう。

アプリ「useref-app」を作成しFocusInputコンポーネントを作ります。

npx create-next-app@latest useref-app

app/components/focusInput.js

"use client";

import { useEffect, useRef, useState } from "react";

function FocusInput() {
    const inputRef = useRef(null);
    const [count, setCount] = useState(0);

    // ✅ コンポーネントがマウントまたは再レンダリングされるたびに alert を表示
    useEffect(() => {
        alert("コンポーネントがレンダリングされました!");
    });

    const handleFocus = () => {
        inputRef.current.focus();
        inputRef.current.value = "フォーカスされました!"; // ✅ `inputRef` を変更
    };

    return (
        <div>
            <input ref={inputRef} type="text" placeholder="Type here..." />
            <button onClick={handleFocus}>フォーカスを当てる(useRef変更)</button>
            <button onClick={() => setCount(count + 1)}>カウント増加(useState変更)</button>
            <p>カウント: {count}</p>
        </div>
    );
}

export default FocusInput;

app/page.js

import Image from "next/image";
import FocusInput from "./components/focusInput";

export default function Home() {
  return (
    <FocusInput />
  );
}

開発サーバーを作り、画面を確認します。

npm run dev

最初に「コンポーネントがレンダリングされました!」とアラートが表示されます。

次に「フォーカスを当てる」をクリックしてもアラートは出ません。

「カウント増加」をクリックすると

アラートが出ます。

このようにuseStateは更新されるごとにコンポーネントが再レンダリングされるのに対して、useRefは更新しても再レンダリングされないことがわかります。

コード解説

コードを解説していきます。

クライアントコンポーネントの宣言、必要な機能のインポートをしています。

"use client";

import { useEffect, useRef, useState } from "react";

使用するstateとrefを設定しています。

function FocusInput() {
    const inputRef = useRef(null);
    const [count, setCount] = useState(0);

useEffectを使ってレンダリングされたときにアラートが出るようにしています。

 useEffect(() => {
        alert("コンポーネントがレンダリングされました!");
    });

ここでは第二引数の[]を省略することで、レンダリングされる度に動くようにしています

レンダリングされる度には動く処理であればuseEffectを使わずに直にalertを記述してもよさそうなものですが、実際これはエラーがでます。理由は後述します。

「フォーカスを当てる」ボタンのクリックイベントのイベントハンドラーを記述しています。

  const handleFocus = () => {
        inputRef.current.focus();
        inputRef.current.value = "フォーカスされました!"; // ✅ `inputRef` を変更
    };

ここではinputRefにフォーカスを当てる処理とvalueの値を「フォーカスされました!」に設定する処理を定義しています。

続けて戻り値のJSXです。

return (
        <div>
            <input ref={inputRef} type="text" placeholder="Type here..." />
            <button onClick={handleFocus}>フォーカスを当てる(useRef変更)</button>
            <button onClick={() => setCount(count + 1)}>カウント増加(useState変更)</button>
            <p>カウント: {count}</p>
        </div>
    );

注目すべきなのは下記です。

<input ref={inputRef} type="text" placeholder="Type here..." />

このように、タグのrefプロパティにuseRefの値を設定することで、このタグをDOM操作できるようにしています。

「フォーカスを当てる」ボタンタグのクリックイベントでこのinputタグをDOM操作できるようにしているわけです。

<button onClick={handleFocus}>フォーカスを当てる(useRef変更)</button>

このように、useRefはDOM操作をするときに使用します。

副作用について

今回のコードの中で、

 useEffect(() => {
        alert("コンポーネントがレンダリングされました!");
    });

という、alertの処理をわざわざuseEffect内に実装する記述がありました。

たとえば、

alert("コンポーネントがレンダリングされました!");

というように、コンポーネント内に直接記述するとエラーが発生します。

これはなぜかというと、このalertの記述が「副作用」であるためです。

副作用ってなんやねん?

React のコンポーネントは基本的に 「入力 → 出力」 という流れで動くシンプルなもの。
たとえば、次のようなコードは 「純粋な処理」 です。

✅ 純粋な処理(副作用なし)

function add(a, b) {
    return a + b;
}
console.log(add(2, 3)); // 5

この処理は「何かを変更したり影響を与えたりしない」ので、スッキリしてる!

💩 副作用のある処理

でも、もしこの関数の中で 「コンソールにログを出す」 とか 「画面のタイトルを変える」 みたいなことをすると…
それは 「副作用(Side Effect)」 になります!

function addWithEffect(a, b) {
    console.log("足し算が実行されました!"); // 💩 これは副作用
    document.title = `合計: ${a + b}`; // 💩 これも副作用
    return a + b;
}
console.log(addWithEffect(2, 3)); // 5

🚨 「足し算をするだけのはずなのに、コンソール出力やタイトル変更をしてる!」
🚨 「本来の目的(足し算)以外のことをしてる!」
🚨 これが useEffect で管理すべき「副作用」なのだ!

React における副作用

React では、コンポーネントの役割は 「UI を描画すること」。
でも、時々 「コンポーネントの外の世界」に影響を与えないといけないことがあります。(副作用)

API リクエストサーバーからデータを取得する(fetch など)
イベントリスナー
window.addEventListener("scroll", ...) を設定
タイマー処理setTimeoutsetInterval を使う

こういった処理はuseEffectで管理することになっています。

少しイメージしづらいですが、ルールとして覚えておきましょう。

コメント

タイトルとURLをコピーしました