useRefで値を更新したりDOMを参照する方法【React/useRef/useState/useEffect/TypeScript】

JavaScript

useRefは、主にDOMを参照したい時に使用しますが、改めて使い方を調べたのでメモしておきます!

useRefとは

useRefは、useStateと同じく値を保持できるフックです。useStateとの主な違いは、値を更新しても再レンダリングされない点です。

構文は次の通りです。※使用するにはuseRefをimportしておきます。

import { useRef } from "react"; //useRefをインポート

const <変数名> = useRef(<初期値>)

上記の構文を当てはめると次のようになります。

// 使用例
const testString = useRef("Hello !");

変数testStringの中身をconsole.log()で確認すると、currentプロパティに設定した初期値が格納されていることが分かります。

useRefの値の取得、更新はcurrentプロパティ経由で行うことになります。

基本的な使い方

基本的な使い方をサンプルで確認します。

【使用例】カウントアップするサンプル

こちらは、ボタンをクリックするとカウントアップするシンプルなサンプルです。

import { useRef } from "react";

export default function App() {
  const count = useRef(0); // countの初期値は0
  const onClickCountUp = () => {
     count.current = count.current + 1; // countの値を1つ増やす
    console.log(count.current);
  }

  return(
    <div className="container">
         <h1>useRefでカウントアップ</h1>
         <button onClick={onClickCountUp}>カウントアップ</button>
        <p>count: {count.current}</p>
        <p>※値の増減はコンソールで確認できます</p>
    </div>
  )
}

実際の動きはこちらで確認できます!

カウントアップボタンをクリックしても、レンダリングされないので画面に変化はありません。
コンソールだと値が変化しているのを確認できます。

【使用例】現在と前回の値を表示させる

先程の挙動から、useRefで更新しても、レンダリングされないことが分かりました。
更新した値を画面に表示させたい場合、何かしらの方法で再レンダリングさせれば良さそうです。

手っ取り早いのはuseStateにuseRefの値を格納することです。条件をつけてレンダリングしたい時に使えそうです。

今回は、useEffectも使って、更新前の値を取得できるサンプルを作りました。

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

export default function App() {
  const prevValue = useRef(0); // 前回の値を格納
  const [currentValue, setCurrentValue] = useState(0); // 現在の値を格納

  // 現在の値に変更があったら実行
  useEffect(() => {
    prevValue.current = currentValue;
  }, [currentValue]);

  // クリックされたら値をプラス1
  const onClickCount = () => {
    setCurrentValue((currentValue) => currentValue + 1);
  }
  return (
    <div className="App">
      <button onClick={onClickCount}>カウントアップ</button>
      <p>現在の値: {currentValue}</p>
      <p>前回の値: {prevValue.current}</p>
    </div>
  );
}

実際の動きはこちらで確認できます!

useEffectは、初回レンダリング時と、監視しているcurrentValueの変更があるたびに実行されます。

  useEffect(() => {
    prevValue.current = currentValue;
  }, [currentValue]);

useRefが更新された時点ではレンダリングされず、useStateが更新された時にレンダリングされます。
この仕組みを使って前回の値を表示させてます。

DOMを参照する

次はuseRefを使ってDOMを参照する方法です。
構文は次の通りになります。※関係ある箇所を色付けしています。

import { useRef } from "react";

export default function App() {
  // useRefの初期値はnull
  const refH1 = useRef(null);

  return (
    <div className="App">
      <h1 ref={refH1}>サンプルタイトル</h1>
    </div>
  );
}

ポイントは、参照したいタグにref={<useRefを格納した変数>}を設定することです。
ここでは変数 refH1に参照したいタグを格納します。
console.log()で中身を確認すると、currentプロパティにタグ情報が格納されているのが分かります。

あとは、通常のJavaScriptと同じように扱うことができます。
例えば、以下はテキストの内容を参照できるtextContent プロパティを参照する例です。

refH1.current.textContent
//結果
サンプルタイトル

タグのテキストを取得できました!
JavaScriptのdocument.querySelector()でDOMを参照することは推奨されていません。
ReactではこのようにuseRefを使ってDOMを参照します。

【利用例】Enterでフォーカスが移動するフォーム

サンプルとしてEnterでフォーカスが移動するフォームを作成しました。
実際に使用する際はsubmitボタンを除外したりと工夫が必要ですが、Enterでフォーカスが移動するのは地味に便利です。

練習も兼ねてTypeScriptで記述してます。ちょっと怪しいコードです、、。

まとめ

useRefはDOMの参照くらいしか使っていませんでしたが、値の更新でレンダリングされない特徴をうまく使えば、色々と使えそうです!

コメント