[React] useRefを理解する！

## useRefとは？
`useRef`とはDOM要素の参照を保存したり、レンダリング間で変更されない値を保持するために利用されます。

ユースケース
- DOM要素の参照を保持
- レンダリング間で変更されない値を保持

## DOM要素の参照を保持する

### 1. `useRef`を利用して変数を作成する

- `useRef`を実行し、戻り値の`RefObject`を変数に取る。
  ```tsx
  const inputRef = useRef<HTMLInputElement>(null);
  ```
  - **引数**: 初期値（ここでは`null`）
  - **ジェネリック**: 保存するReact要素の型（ここでは`HTMLInputElement`）

### 2. `ref`プロパティを使用してDOM要素に`RefObject`を指定する

- `input`要素に`ref`プロパティを使用し、`RefObject`を指定する
  ```tsx
  <input type="text" ref={inputRef} />
  ```

### 3. DOM要素にアクセスする

`RefObject`に保存した値は`current`プロパティに保存されています。
  ```tsx
  const handleClick = () => {
    if (inputRef.current) {
      inputRef.current.focus();
    }
  };

  return (
    <div>
      <input type="text" ref={inputRef} />
      <button onClick={handleClick}>フォーカス</button>
    </div>
  );
```

## :warning: useRefの注意点

`current`プロパティに参照が保存されるタイミングは、コンポーネントがマウントされたタイミングになります。
> `componentDidMount` 

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2842674/2bd1eba6-af90-06a6-5a51-e2115a6164ee.png)


引用元

https://projects.wojtekmaj.pl/react-lifecycle-methods-diagram/

そのため、コンポーネントマウント前に`current`プロパティにアクセスすると、`null`が返されます。
```tsx
const Sample: React.FC = () => {
  const inputRef = useRef<HTMLInputElement>(null);
  console.log(inputRef.current); // => null

  return (
    <>
      <input type="text" ref={inputRef} />
    </>
  );
};
```

対処法として、以下2つの方法があります。
1. `useEffect`を利用する
1. `ref`コールバックを利用する

### useRef + useEffect
`useEffect`はコンポーネントのマウント後に実行されるため、`current`プロパティに値が格納された後に実行することができます。

```tsx
const Sample: React.FC = () => {
  const inputRef = useRef<HTMLInputElement>(null);
  console.log(inputRef.current); // => null

  useEffect(() => {
    console.log(inputRef.current); // => <input type="text">
  }, []);

  return (
    <>
      <input type="text" ref={inputRef} />
    </>
  );
};
```

### useRef + refコールバック
`ref`属性にコールバック関数を渡す方法です。
関数はコンポーネントのマウント時と、アンマウント時に実行されます。
アンマウント時は、引数に`null`を与えて関数を呼び出します。

呼び出しは初回のみで良いため、`useCallback`を使用して、メモ化しています。

```tsx
const Sample: React.FC = () => {
  const inputRef = useRef<HTMLInputElement>(null);
  console.log(inputRef.current); // => null

  const inputRefCallback = useCallback((input: HTMLInputElement | null) => {
    if (input === null) return;
    console.log(input); // => <input type="text">
  }, []);

  return (
    <>
      <input type="text" ref={inputRefCallback} />
    </>
  );
};
```

参考にさせていただいた記事

https://qiita.com/uhyo/items/abacedd7d4b8370688c5

## レンダリング間で変更されない値を保持する

### 1. useRefを利用して変数を作成する

- `useRef`を実行し、変数を作成
  ```tsx
  const countRef = useRef(0);
  ```

### 2. 変数の値を更新する

- `current`プロパティを使用して、変数の値を更新
  ```tsx
  const incrementCount = () => {
    countRef.current += 1;
    console.log(countRef.current);
  };
  ```

### 3. 変数の値を使用する

- 変数の値はコンポーネントが再レンダリングされても保持される
```tsx
  const MyComponent = () => {
    const countRef = useRef(0);

    const incrementCount = () => {
      countRef.current += 1;
      console.log(countRef.current);
    };

    return (
      <div>
        <p>レンダリング間で変更されない値: {countRef.current}</p>
        <button onClick={incrementCount}>カウントアップ</button>
      </div>
    );
  };
```

## まとめ

- `useRef`は、DOM要素の参照を保持するために利用される
- 上記とは別に、再レンダリングされても値がリセットされない変数を保持するためにも使用される
- `RefObject.current`で値を読み書きし、必要に応じてその値を利用する