【React】 refを使って要素と紐づける

Reactでは、refを利用してDOMの要素を簡単に変数に格納することができます。

Refを試す

まずはuseRefを使用してRefを作成し、それをDOMと紐づけてみます。

import { useEffect } from 'react';

export default function App() {
    const elem = useRef();

    return (
        <>
        <div ref={elem}>
            <p>hoge</p>
        </div>
        </>
    );
}

これでひも付きました。値を見てみましょう。

export default function App() {
    const textInput = useRef();

    useEffect(() => {
        console.log(elem);
        console.log(elem.current.innerHTML);
    });

    // ...
}
current: div
    __reactFiber$h6k35lt4jzh: FiberNode {tag: 5, key: null, elementType: 'div', type: 'div', stateNode: div, …}
    __reactProps$h6k35lt4jzh: {children: {…}}
    accessKey: ""
    align: ""
    ariaAtomic: null
    ariaAutoComplete: null
    ...

<p>hoge</p>

このように、useRefで作成したrefは、.currentで格納されている値にアクセスできます。その値を見ると、普通のJavascriptで要素を取得した時と同じ値が入っています。

使う場面

DOMそのものが必要になることは少ないです。基本的にはJSXの構文を用いて表示状態などを制御します。

必要になる場面としては、

  • Reactで制御できない要素を操作する (input:fileなど)
  • React用ではない外部のライブラリなどを使用する (jQueryやアニメーション関連のものなど)

などに用います。つまり、Reactの機能だけでは制御できず、DOM自体が必要な場合に用います。

import { useRef } from 'react';
import { useEffect } from 'react';
import $ from "jquery";

export default function App() {
    const elem = useRef();
    const btn = useRef();

    // jQueryでクリックイベントを登録
    useEffect(() => {
        const $btn = $(btn.current);
        $btn.on("click", function () {
            alert(elem.current.innerHTML);
        });

        return () => {
            $btn.off("click");
        }
    }, []);
    
    return (
        <>
        <div ref={elem}>
            <p>hoge</p>
            <button ref={btn}>送信サンプル</button>
        </div>
        </>
    );
}

しかし、これを乱用してしまってはReactを使用している意味が薄れてしまいます。Reactの機能だけでは賄えず、外部ライブラリを頼りたい等の場合に絞っていきましょう。

useEffect内で使用しているのは、宣言した直後の時点ではまだnullであるためです。useEffectを用いることでレンダリング後に実行でき、その時点ならrefで指定した変数に値が格納されています。

宣言的とは

Reactの公式ドキュメントを見ると、

宣言的に行えるものには ref を使用しないでください。

と書かれています。これはどういうことなのでしょうか。

宣言的(Declarative)とは、簡単に言うと抽象的な操作です。例えば、xx円のものを買う、水を飲む、電気を消す、などといったものです。

逆に命令的(Imperative)というと、具体的な細かい操作です。例えば、ものを買うのに財布を取り出して、そこから札や硬貨の組み合わせを決めて、お金を取り出して置いて、所持金を減算して… といった形です。

つまり、宣言的にできるものなら、refは使わないほうが良い (というより使う必要がない) ということです。裏でどのように動くべきかが既に提供されているので、車輪の再発明をする必要はありません。

どうしても命令的に書かなければならない場合や、Reactではできない操作を外部ライブラリなどで行う場合に使用します。例えばinput:fileの管理はReactでは困難なので、要素自体を見て操作する必要があります。

Ref自体は他でも使える

上ではrefで作った変数と要素を紐づけました。これは、別に要素でなくても良いです。

export default function App() {
    const v = useRef(10);
    
    return (
        <>
        <p>{v.current}</p>
        <button 
            onClick={() => {v.current += 10}}
        >
            +10
        </button>
        </>
    );
}

しかし、useStateと違い、値を更新しても再レンダされません。とはいえ値自体は更新されており、次のようにuseStateした値を更新するなどで再レンダされると、ref側の値も内部では更新されているということがわかります。

export default function App() {
    const v = useRef(10);
    const [v2, setV2] = useState(10);
    
    return (
        <>
        <p>{v.current}</p>
        <button onClick={() => {v.current += 10}}>
            v+10
        </button>

        <p>{v2}</p>
        <button onClick={() => {setV2(v2 + 10)}}>v2+10</button>
        </>
    );
}

しかし、refという名称から、基本的には要素の参照を持っておくべきであるというのは何となく分かると思います。この内容は特殊な利用方法であるということを把握しておきましょう。

まとめ

useRefを用いて作成した変数を、JSXのref属性に入れることで、変数と要素を紐づけることができます。

紐づけたら普通の要素自体が紐づき、React用向きではない外部ライブラリなどと組み合わせることが可能になります。しかし、そういった特殊用途以外では利用するべきではありません。Reactの機能や拡張するライブラリで片付く場合はそれを利用していきましょう。

react useref thumb

役に立ったらシェアしよう!