【React】useRef ~2つの使い方とその背景~

## はじめに
あるinput要素が画面に表示された時に、デフォルトでフォーカスをあててinput要素をクリックしなくても入力できる状態にしたいという場面があり、Reactでは`useRef`というhooksを使ってこれを実装できることを知った。

さらに調べてみると、`useRef` の活用方法や背景は興味深いものだったので、記事にまとめておく。


## useRefとは
ドキュメントから抜粋すると、以下のように紹介されている。
> 本質的に `useRef` とは、書き換え可能な値を `.current` プロパティ内に保持することができる「箱」のようなものです。
> 

https://ja.reactjs.org/docs/hooks-reference.html#:~:text=%E6%9C%AC%E8%B3%AA%E7%9A%84%E3%81%AB%20useRef%20%E3%81%A8%E3%81%AF%E3%80%81%E6%9B%B8%E3%81%8D%E6%8F%9B%E3%81%88%E5%8F%AF%E8%83%BD%E3%81%AA%E5%80%A4%E3%82%92%20.current%20%E3%83%97%E3%83%AD%E3%83%91%E3%83%86%E3%82%A3%E5%86%85%E3%81%AB%E4%BF%9D%E6%8C%81%E3%81%99%E3%82%8B%E3%81%93%E3%81%A8%E3%81%8C%E3%81%A7%E3%81%8D%E3%82%8B%E3%80%8C%E7%AE%B1%E3%80%8D%E3%81%AE%E3%82%88%E3%81%86%E3%81%AA%E3%82%82%E3%81%AE%E3%81%A7%E3%81%99%E3%80%82

### 主な使用方法は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要素にフォーカスを当ててみる。

```php
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要素にフォーカスが当たっていることが確認できる。

https://codesandbox.io/s/useref-t8khx

### useRef の構文

```javascript
const オブジェクトを受け取る変数 = useRef(初期値)
```

useRefから返されるオブジェクトは `current` というプロパティを持ち、この `current` では**書き換え可能**な値を持つことが出来る。

上記の例では、以下のように`inputRef`の`current`プロパティにinput要素を設定し、JavaScriptを使ってその要素を操作したことになる。

```php
// useRefの初期化
const inputRef = useRef(null);

// inputRefのcurrentプロパティにinput要素を設定する
<input ref={inputRef} />
```

また、上記の通りinput要素にrefを指定した状態で以下のように記述すれば、input要素の中身を操作することもできる。

```javascript
inputRef.current.value = 'あいうえお';
```

## refとuseRefの背景

次に「使用方法②：値を保持する」の説明に入る... 前に、useRefができた背景を知って筆者は理解が深まったので、先に紹介する。

#### hooks誕生前
    
`useRef`という`hooks`を使って、より便利にrefを参照できるようになったわけだが、ref自体はhooksが生まれる前からDOMやReact要素にアクセスする方法として提供されてきた。
    
> Ref は render メソッドで作成された DOM ノードもしくは React の要素にアクセスする方法を提供します。
> 
    
https://ja.reactjs.org/docs/refs-and-the-dom.html

    
#### hooks誕生後
    
`useRef` は単純なDOMへのアクセス（使用方法①）だけでなく、値を保持する（使用方法②）という用途も提供する。
    
> しかしながらuseRef() は ref 属性で使うだけではなく、より便利に使えます。これはクラスでインスタンス変数を使うのと同様にして、あらゆる書き換え可能な値を保持しておくのに便利です。 
    
https://ja.reactjs.org/docs/hooks-reference.html#:~:text=%E3%81%97%E3%81%8B%E3%81%97%E3%81%AA%E3%81%8C%E3%82%89%20useRef()%20%E3%81%AF%20ref%20%E5%B1%9E%E6%80%A7%E3%81%A7%E4%BD%BF%E3%81%86%E3%81%A0%E3%81%91%E3%81%A7%E3%81%AF%E3%81%AA%E3%81%8F%E3%80%81%E3%82%88%E3%82%8A%E4%BE%BF%E5%88%A9%E3%81%AB%E4%BD%BF%E3%81%88%E3%81%BE%E3%81%99%E3%80%82%E3%81%93%E3%82%8C%E3%81%AF%E3%82%AF%E3%83%A9%E3%82%B9%E3%81%A7%E3%82%A4%E3%83%B3%E3%82%B9%E3%82%BF%E3%83%B3%E3%82%B9%E5%A4%89%E6%95%B0%E3%82%92%E4%BD%BF%E3%81%86%E3%81%AE%E3%81%A8%E5%90%8C%E6%A7%98%E3%81%AB%E3%81%97%E3%81%A6%E3%80%81%E3%81%82%E3%82%89%E3%82%86%E3%82%8B%E6%9B%B8%E3%81%8D%E6%8F%9B%E3%81%88%E5%8F%AF%E8%83%BD%E3%81%AA%E5%80%A4%E3%82%92%E4%BF%9D%E6%8C%81%E3%81%97%E3%81%A6%E3%81%8A%E3%81%8F%E3%81%AE%E3%81%AB%E4%BE%BF%E5%88%A9%E3%81%A7%E3%81%99%E3%80%82


　
このように、先に説明した使用方法①は従来からの`ref`の使用法であり、次に説明する使用方法②は`useRef`という`hooks`が誕生したことにより、ref属性をこんな風にも使えますよ〜〜と紹介されている機能といえる。

## 使用方法②：値を保持する

最初にも少し紹介したが、`useRef`が返すオブジェクトの`current`プロパティは書き換え可能な値を保持することが出来る。

`useState`も値を保持することが出来るが、`useState`と違い`useRef`では値を更新してもコンポーネントの再レンダリングが起きない。

> `useRef` は中身が変更になってもそのことを**通知しない**ということを覚えておいてください。`.current` プロパティを書き換えても再レンダーは発生しません。
> 

https://ja.reactjs.org/docs/hooks-reference.html#:~:text=%E3%81%A8%E3%81%84%E3%81%86%E3%81%93%E3%81%A8%E3%81%A7%E3%81%99%E3%80%82-,useRef,-%E3%81%AF%E4%B8%AD%E8%BA%AB%E3%81%8C

### useStateとuseRefの挙動をくらべる

`useState`と`useRef`、それぞれが保持する値を更新した時の挙動を比べてみる。

```php
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`の値を更新すると毎回レンダリングが起きることが分かる。

https://codesandbox.io/s/useref-value-vm9un?file=/src/App.js

## 最後に

私が今回業務で`useRef`を使用したのは、使用方法①の使い方のみだった。
使用方法②についても、挙動は理解したが具体的な活用シーンが特に思い浮かんでいないので、有効に活用されているコードを見てみたい。

また今回TypeScriptで実装したのだが、`useRef`を使う際の型にもルールがあることを学んだので、改めて別の記事にまとめようと思う。


## 参考記事

https://zenn.dev/berlysia/articles/624bc1aaffda58#%E3%82%A4%E3%83%B3%E3%82%B9%E3%82%BF%E3%83%B3%E3%82%B9%E5%A4%89%E6%95%B0%E4%BB%A3%E3%82%8F%E3%82%8A%E3%81%AB%E4%BD%BF%E3%81%86%E3%81%A8%E3%81%8D

https://natural-tearoom.com/hook-useref/

https://reffect.co.jp/react/react-hook-useref#input