はじめに
あるinput要素が画面に表示された時に、デフォルトでフォーカスをあててinput要素をクリックしなくても入力できる状態にしたいという場面があり、ReactではuseRef
というhooksを使ってこれを実装できることを知った。
さらに調べてみると、useRef
の活用方法や背景は興味深いものだったので、記事にまとめておく。
useRefとは
ドキュメントから抜粋すると、以下のように紹介されている。
本質的に
useRef
とは、書き換え可能な値を.current
プロパティ内に保持することができる「箱」のようなものです。
主な使用方法は2種類
-
DOMへのアクセス
例:input要素にフォーカスする -
値を保持する
useStateとの大きな違いは、値を更新しても再レンダリングを起こさないこと。
使用方法①:DOMへのアクセス
useRef
・ref
を使うと、DOMへアクセスすることができる。
例えば、特定のinput要素にアクセスし、その要素に対してJavaScriptのfocusメソッドを使ってフォーカスをあてるというように使う。
<input id="name" />
のinput要素にフォーカスしたい場合、JavaScriptでは document.getElementById('name').focus()
というように書くが、これと同じことをReactで行いたい場合にuseRefを使う。
使ってみる
通常input要素に入力をするには、画面が描画されたらinput要素をクリックするなどしてフォーカスを当てなければいけない。
そこでuseRefを使い、画面描画された時点でinput要素にフォーカスを当ててみる。
import { useEffect, useRef } from 'react'
export default function App() {
const inputRef = useRef(null) // 1. useRefの初期化
console.log(inputRef)
useEffect(() => {
inputRef.current.focus() // 3. 使用する
console.log(inputRef)
},[])
return (
<div className="App">
<input ref={inputRef} /> // 2. ref属性に設定
</div>
);
}
以下のcodesandboxのサンプルコードの通り、表示した時点でinput要素にフォーカスが当たっていることが確認できる。
useRef の構文
const オブジェクトを受け取る変数 = useRef(初期値)
useRefから返されるオブジェクトは current
というプロパティを持ち、この current
では書き換え可能な値を持つことが出来る。
上記の例では、以下のようにinputRef
のcurrent
プロパティにinput要素を設定し、JavaScriptを使ってその要素を操作したことになる。
// useRefの初期化
const inputRef = useRef(null);
// inputRefのcurrentプロパティにinput要素を設定する
<input ref={inputRef} />
また、上記の通りinput要素にrefを指定した状態で以下のように記述すれば、input要素の中身を操作することもできる。
inputRef.current.value = 'あいうえお';
refとuseRefの背景
次に「使用方法②:値を保持する」の説明に入る... 前に、useRefができた背景を知って筆者は理解が深まったので、先に紹介する。
hooks誕生前
useRef
というhooks
を使って、より便利にrefを参照できるようになったわけだが、ref自体はhooksが生まれる前からDOMやReact要素にアクセスする方法として提供されてきた。
Ref は render メソッドで作成された DOM ノードもしくは React の要素にアクセスする方法を提供します。
hooks誕生後
useRef
は単純なDOMへのアクセス(使用方法①)だけでなく、値を保持する(使用方法②)という用途も提供する。
しかしながらuseRef() は ref 属性で使うだけではなく、より便利に使えます。これはクラスでインスタンス変数を使うのと同様にして、あらゆる書き換え可能な値を保持しておくのに便利です。
このように、先に説明した使用方法①は従来からのref
の使用法であり、次に説明する使用方法②はuseRef
というhooks
が誕生したことにより、ref属性をこんな風にも使えますよ〜〜と紹介されている機能といえる。
使用方法②:値を保持する
最初にも少し紹介したが、useRef
が返すオブジェクトのcurrent
プロパティは書き換え可能な値を保持することが出来る。
useState
も値を保持することが出来るが、useState
と違いuseRef
では値を更新してもコンポーネントの再レンダリングが起きない。
useRef
は中身が変更になってもそのことを通知しないということを覚えておいてください。.current
プロパティを書き換えても再レンダーは発生しません。
useStateとuseRefの挙動をくらべる
useState
とuseRef
、それぞれが保持する値を更新した時の挙動を比べてみる。
import { useState, useRef } from "react";
export default function App() {
const [countState, setCountState] = useState(0);
const countRef = useRef(0);
const handleStateClick = () => setCountState(countState + 1);
const handleRefClick = () => countRef.current++;
console.log("レンダリング!");
return (
<div className="App">
<p>{countState}</p>
<button onClick={handleStateClick}>state +1</button>
<p>{countRef.current}</p>
<button onClick={handleRefClick}>ref +1</button>
</div>
);
}
以下のcodesandboxのサンプルコードで挙動を確認してみると、useRef
の値を更新してもレンダリングが起きないが、useState
の値を更新すると毎回レンダリングが起きることが分かる。
最後に
私が今回業務でuseRef
を使用したのは、使用方法①の使い方のみだった。
使用方法②についても、挙動は理解したが具体的な活用シーンが特に思い浮かんでいないので、有効に活用されているコードを見てみたい。
また今回TypeScriptで実装したのだが、useRef
を使う際の型にもルールがあることを学んだので、改めて別の記事にまとめようと思う。
参考記事