React hooksを基礎から理解する (useRef編)

#React hooksとは

React 16.8 で追加された新機能です。
クラスを書かなくても、 `state`などのReactの機能を、関数コンポーネントでシンプルに扱えるようになりました。

- [React hooksを基礎から理解する (useState編)](https://qiita.com/seira/items/f063e262b1d57d7e78b4)
- [React hooksを基礎から理解する (useEffect編)](https://qiita.com/seira/items/e62890f11e91f6b9653f)
- [React hooksを基礎から理解する (useContext編)](https://qiita.com/seira/items/fccdf4e73c59c491558d)
- [React hooksを基礎から理解する (useReducer編)](https://qiita.com/seira/items/2fbad56e84bda885c84c)
- [React hooksを基礎から理解する (useCallback編)](https://qiita.com/seira/items/8a170cc950241a8fdb23)
- [React hooksを基礎から理解する (useMemo編)](https://qiita.com/seira/items/42576765aecc9fa6b2f8)
- React hooksを基礎から理解する (useRef編)  :point_left_tone2: 今ここ


## useRefとは

関数コンポーネントでは、Classコンポーネント時のref属性の代わりに、`useRef`を使って要素への参照を行います。
また`useRef`では、`useState`のようにコンポーネント内での値を保持することが出来ます。

### 構文

```jsx
const refObject = useRef(initialValue)

//例
const number = useRef(100);
console.log(number.current); // 100
```
`useRef`は、`.current`プロパティが渡された引数（初期値はinitialValue）を`refObject`へ返します。
この引数の値が書き換え可能な`.current`プロパティーの値であり、 `.current`プロパティ内に保持することができます。

#### DOMを参照したい場合

```jsx
const inputElement = useRef(null)

//例: inputElement.currentで <input type="text" /> を参照
<input ref={inputElement} type="text" />
console.log(inputElement.current); // <input type="text" />
```
##### DOMの参照例

`useRef`でrefオブジェクトを作成したものをref属性（HTML要素）に指定してDOMを参照しています。

```jsx
const App = () => {
  const inputEl = useRef(null);
  const handleClick = () => {
    inputEl.current.focus();
    console.log("inputEl.current:", inputEl.current);
    //inputEl.current: <input type="text">
  };
  return (
    <>
      <input ref={inputEl} type="text" />
      <button onClick={handleClick}>入力エリアをフォーカスする</button>
    </>
  );
};
```

ボタンクリックで`<input type="text">`がfocusされました。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/218656/1d9adc04-3479-4940-886b-abeaf7a33789.gif" width=300 />
参照：[React公式サイト](https://ja.reactjs.org/docs/hooks-reference.html#useref)

#### useRefとuseStateをくらべてみる

##### useRefを使ってDOMを参照
useRefを利用すると`text`のstate更新時にのみコンポーネントの再レンダリングが発生します。

```jsx
const App = () => {
  const inputEl = useRef(null);
  const [text, setText] = useState("");
  const handleClick = () => {
    setText(inputEl.current.value);
  };
  console.log("レンダリング！！");
  return (
    <>
      <input ref={inputEl} type="text" />
      <button onClick={handleClick}>set text</button>
      <p>テキスト : {text}</p>
    </>
  );
};
```
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/218656/795fe524-0716-918c-2f74-b92635aa6542.gif" width=350 />
※ コンソールに表示される文字列： （誤り） 描画！！　=> （正） レンダリング！！

##### useStateを使ってDOMを参照

入力中の文字列をステート`inputElement`に格納、ボタンが押された時に`setText(inputElement)`で`text`stateへ代入することで`useRef`を使った時と同じ挙動にしています。この場合、`text`と`inputElement`のstate更新時の両方でコンポーネントの再レンダリングが発生しています。

```jsx
const App = () => {
  const [inputElement, setInputElement] = useState("");
  const [text, setText] = useState("");
  const handleClick = () => {
    setText(inputElement);
  };
  console.log("レンダリング！！");
  return (
    <>
      <input
        value={inputElement}
        onChange={(e) => setInputElement(e.target.value)}
        type="text"
      />
      <button onClick={handleClick}>setText</button>
      <p>テキスト : {text}</p>
    </>
  );
};
```
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/218656/175c1e6e-3041-909e-8880-550decb083cf.gif" width=350 />
※ コンソールに表示される文字列： （誤り） 描画！！　=> （正） レンダリング！！


`useState`を利用している場合はstateの変更される度にコンポーネントの再レンダリングが発生しますが、`useRef`は値が変更になっても、コンポーネントの再レンダリングは発生しませんでした:relaxed:

コンポーネントの再レンダリングはしたくないけど、内部に保持している値だけを更新したい場合は、保持したい値を`useState`ではなく、`useRef`を利用するのが良さそうです。