Reactのhooksについてちゃんと理解する ~State・Context・Ref編~

## はじめに

Reactにおいてhooksはとても重要な要素ですが、これまであまり理解できずに利用していた節があるのでドキュメントを読んでちゃんと理解しようと思います。

## そもそもhooksとは

コンポーネントから利用できるReactの様々な機能のことで、Reactに組み込みのものや自分で作成したものを利用することができます。

## hooksの注意点

- hooksはReactがレンダーされている間のみ利用することが可能
- コンポーネントのトップレベルまたはカスタムフック内でのみ呼び出すことが可能
    - 条件分岐、ループ、ネストされた関数の中でフックを呼び出すことはできない

## Stateフック

stateを利用すると情報をコンポーネントに記憶させることができます。
**コンポーネント**にという部分が重要で、stateは別コンポーネントでは利用できないため、別コンポーネントでも利用したい場合は別の方法を利用する必要があります。([stateのリフトアップ](https://ja.react.dev/learn/sharing-state-between-components)など)
stateは個々のコンポーネントインスタンスに対してローカルなため、同じコンポーネントを2回利用したとしてそのコンポーネント間の状態が共有されることはありません。

- `useState`
    - state変数とstateを変更するsetter関数を提供する
    - stateを変更したときは再レンダーがトリガーされる
    - `const [test, setTest] = useState();` でstateを宣言する
    - set関数で変更した値が、以前のstateと同じ場合コンポーネントと、その子コンポーネントの再レンダーをスキップする
    - stateの更新をまとめて行うため、stateが更新された瞬間に再レンダーを行うのではなく、すべてのイベントが終了した後にレンダーを行う
    - set関数は現在実行中のstateを変更するわけではなく、次のレンダー以降にuseStateが返却する値のみに影響を与える
        - つまり、同じ実行中の別の箇所でstateを利用しようとしてもまだstateは変更されていないので以前のstateが返却される
        - 以前のstateを利用して新たなstateをセットするという場合にバグが起こりうる
            - https://ja.react.dev/reference/react/useState#updating-state-based-on-the-previous-state
- `useReducer`
    - stateとdispatch関数を返却する
        - dispatch関数とは
            - dispatch関数に渡されたアクションを用いて、useReducer()の引数に指定したreducer関数を呼び出した結果になる
    - 慣例としてswitch文として書くことが一般的
    - stateをsetterで更新するわけではなく、reducer関数が返却する値がstateになる

### useStateとuseReducerの違いは？

状態を直接変更するか、dispatch関数を経由して変更するかの違いです。細かくみるとコードサイズ・可読性・デバック・テストなどの観点で異なってきます。詳しくは以下のドキュメントを参照ください。

https://ja.react.dev/learn/extracting-state-logic-into-a-reducer#comparing-usestate-and-usereducer

### 通常の変数とstateの違いは？

通常の変数はレンダー間で保持されないため、再レンダーされると状態がリセットされてしまいます。また変数が変更されても再レンダーされないため、コンポーネントと変数に差異が生じることになってしまいます。
stateを利用することで上記のような問題を解決することができます

https://ja.react.dev/learn/state-a-components-memory

## Contextフック

Contextフックを利用することでコンポーネントはpropsを利用することなく、離れた親要素から情報を取得できます。通常、親要素の情報を取得するにはpropsを利用し、情報の受け渡しをする必要がありますが、Contextフックを利用することで簡単に受け渡しをすることができるようになります。

- `useContext`
    - コンポーネントのトップレベルで呼び出すことでコンテクストを読み取り、サブスクライブする
    - useContextを利用する前に以下のような手順を踏む必要がある
        - createContextでコンテクストを作成する
            - https://ja.react.dev/reference/react/createContext
        - コンテクストを提供する
            - useContextを利用するコンポーネントの親コンポーネントでproviderとしてコンテキストを提供する
    - `<xxx.Provider value={}>` のvalueに値を入れることでコンテクストを上書きできる
        - providerにvalueを指定しない場合undefinedになるので注意

## Refフック

refを利用するとレンダーに用いない情報を保存できます。stateと違いrefの値を更新してもコンポーネントは再レンダーされません。ブラウザAPIなどReact外のシステムを取り扱うときに役立ちます。

- `useRef`
    - 引数として唯一のプロパティであるcurrentに格納する初期値を受け取る
    - 実際にこのrefを利用する際にはcurrentプロパティにアクセスすると利用できる
    - useRefを変更する場合はsetterではなく `ref.current = 1` のようにcurrentプロパティを手動で更新する
- `useImperativeHandle`
    - コンポーネントが公開するrefをカスタマイズできる
    - forwardRefと一緒に利用する
    - 第1引数としてforwardRef関数から2番目の引数として受け取ったrefを受け取る
    - 第2引数として公開したいrefハンドルを返す関数を受け取る

### 普通の変数との違いは？

refはstateと同様にReactによって再レンダー間で保持されますが、変数はレンダーするごとにリセットされてしまいます。

### vueのrefとの違いは？

vueにおけるrefはリアクティブな状態を宣言するために利用される関数です。似たような名前ですが、ライブラリによって役割が違うので注意が必要です。

https://ja.vuejs.org/guide/essentials/reactivity-fundamentals.html

## 参考
https://ja.react.dev/reference/react/hooks

https://ja.vuejs.org/guide/essentials/reactivity-fundamentals.html