これからReact触るなら確実に抑えておきたいuseOO

# はじめに
開発インターンをしています川端([@haru1125632](https://twitter.com/haru1125632
))です。
PharmaXのアドベントカレンダーの6日目を担当します。
PharmaXではフロントはNext.js(Reactのフレームワーク)を用いて作業しています
そこで今回はReactにまつわる記事を書きました。

# 概要
Reactにはさまざまなuse〇〇が用意されています。
ただし中には利用頻度の低いものからすごく利用するものまであります。
なので今回は以下の一覧から個人的によく使うもの3つに絞って詳しく見ていきたいと思います。
下記の一覧で斜線をひいているものは今回の対象からは除きます。

※ React上級者のかたはクイズだけやってみてください。もし、できなければ記事を読んでください。

ReactHook(use〇〇)一覧 (以下ではReactHookと呼びます)
*中にはRFCのものも含みます
- **useCallback**
- ~~useContext~~  
- ~~useDebugValue~~
- ~~useDeferredValue~~
- **useEffect**
- ~~useEvent~~
- ~~useId~~
- ~~useImperativeHandle~~
- ~~useInsertionEffect~~
- ~~useLayoutEffect~~
- ~~useMemo~~
- ~~useReducer~~
- ~~useRef~~
- **useState**
- ~~useSyncExternalStore~~
- ~~useTransition~~
----
# React Hookを使う上での共通ルール
Hookはcomponentのトップレベルまたは独自のフックでのみ呼び出すことができます。

つまり以下のCodeはトップレベルでなく関数内で呼び出しているため`Error`になります
```react
import React from 'react';
import { useState } from 'react';
export function App(props) {

  const handleHook = () => {
    const [isShow, setIsShow] = useState(false);
  };
  return (
    <div className='App'>
      <button onClick={handleHook}>押す</button>
    </div>
  );
}
```
**Error**
```
Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
1. You might have mismatching versions of React and the renderer (such as React DOM)
2. You might be breaking the Rules of Hooks
3. You might have more than one copy of React in the same app
See https://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem.
```
---

# useState
1番有名なReactHookから紹介します

https://beta.reactjs.org/apis/react/useState

> useState is a React Hook that lets you add a state variable to your component.



### どういった時必要なのか

通常のlocal変数では不十分なケース

```react
import React from 'react';

export function App(props) {
  let count = 0;

  const onClickPlusOne = () => {
    count = count + 1;
    console.log(count)
  };

  return (
    <div className='App'>
      <h1>{count}</h1>
      <button onClick={onClickPlusOne}>＋１</button>
    </div>
  );
}
```
上記は変数`count`を表示して、ボタンを押すと`count`が１ずつ足されていき画面で表示されている`count`が１ずつ増えていくことを期待したCodeです。
これは正しく動作しません.しかし、consoleを見るとbuttonを押すたびにcountの数は増加していることがわかります。

なぜ動作しないのでしょうか?

1. ローカル変数が変更されても再レンダリングされることはありません。つまり、ローカル変数の変更は再レンダリングのトリガーになりません。
2. Reactコンポーネントは2回目レンダリングする際はそのcomponentの最初から読み込み直しますが、ローカル変数の変更は考慮していません

https://beta.reactjs.org/learn/state-a-components-memory

上記のcodeをうまく動作させるには次のようなものが必要です。
1. レンダリング後も状態を必ず保持してくれるもの
2. 再レンダリングをトリガーするようなもの

 これらを実現してくれるのが**useState**なのです。

さっきのCodeを**useState**を用いて書き換えるとこうなります。
```react
import React from 'react';
import { useState } from 'react';

export function App(props) {
  const [count, setCount] = useState(0);

  const onClickPlusOne = () => {
    setCount(count + 1);
  };

  return (
    <div className='App'>
      <h1>{count}</h1>
      <button onClick={onClickPlusOne}>＋１</button>
    </div>
  );
}
```

以下では+ボタンを押すたびにcountの値が変化します。
```react
const [count, setCount] = useState(0);
```
`count`は状態変数で、`setCount`はsetter関数です。
1. rendering後も保存した値を保つ状態変数            `count`
2. Reactをトリガーしてコンポーネントを再レンダリングさせるsetter関数 `setCount`

また唯一の引数である０は状態変数`count`の初期値です。

実際にはどのように行われているかを次に示します。

#### ① 引数の初期値である0を状態変数の0に代入します
#### ② count(0)を画面上に表示します
#### ③ Userがボタンをクリックします。
#### ④ countに０を読み取り、1を追加します
#### ⑤ コンポーネントを再レンダリングします

![スクリーンショット 2022-11-17 9.11.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1980538/b3b02df2-60f8-fa40-aa12-10deac40e6fd.png)

---- 

## クイズ
このような手順がわかっていれば次のconsoleに表示されるものがわかるはずです。
```react
import React from 'react';
import {useState} from 'react'

export function App(props) {
  const [count, setCount] = useState(0);

  const onClickPlusOne = () => {
    setCount(count + 1);
    // Q. このlogには何が出力されるでしょう?
    // 1. 0
    // 2. 1
    console.log(count) 
  };

  // Q2. button押した後このlogには何が出力されるでしょう
  // 1. Toplevel： 0
  // 2. Toplevel: 1
  console.log(`Toplevel: ${count}`);

  return (
    <div className='App'>
      <h1>{count}</h1>
      <button onClick={onClickPlusOne}>＋１</button>
    </div>
  );
}
```

<details><summary>正解をみる</summary>
正解は    <br/>
Q1. 0    <br/>  
Q2. Toplevel: 1
</details>

> set関数を呼び出しても、既に実行中のコードの現在の状態は変更されません。

補足
一度だけ変更されるflagのようなものは`useReducer`を使う方が最適のようです。

https://zenn.dev/spacemarket/articles/9eb80496fa5fe6

-----

# useEffect

> useEffectコンポーネントを外部システムと同期できる React Hook です。

ここでいう外部システムとはブラウザ-API, ネットワークや　サードパーティーライブラリのことです。
Effectを使用すると、特定のイベント(Click Hoberなど..)ではなくレンダリング自体によって引き起こされる副作用を指定できます。


`useEffect`を使用する際は、本当にuseEffectが必要かどうかを検討する必要があります。

> In React, side effects usually belong inside event handlers. Event handlers are functions that React runs when you perform some action—for example, when you click a button. 

通常副作用はEventHandlerに属しています。イベントハンドラーとはボタンをクリックした際に、用意されているonClickなどのことです。

> If you’ve exhausted all other options and can’t find the right event handler for your side effect, you can still attach it to your returned JSX with a useEffect call in your component. This tells React to execute it later, after rendering, when side effects are allowed. However, this approach should be your last resort.

それらのイベントハンドラーを全て調査して、どのようにしてもうまくいかなかった場合のみ `useEffect`を使ってください。　`useEffect`は最終手段です。

https://beta.reactjs.org/learn/you-might-not-need-an-effect

#### なぜuseEffectが必要なのか

今回は以下のようなことを実現します

- コンポーネントが最初にレンダリングされた際にAPIを叩く
- 叩かれたAPIのdataを使って、画面にresponseの配列の1番目のUserの情報を表示する

まず、useEffectを使わないで書いた例を紹介します

```react
import React from 'react';
import { useState, useEffect } from 'react';
const API_URL = 'https://jsonplaceholder.typicode.com/users';

export function App(props) {
  const [user, setUser] = useState({});

  fetch(API_URL)
    .then(res => res.json())
    .then(data => setUser(data[0]))

  return (
    <div className='App'>
      <ul>
        <li>{user.name}</li>
        <li>{user.email}</li>
      </ul>
    </div>
  );
}
```

### 何が問題なのか

dataを表示でき、問題のないように見えます。しかし、APP関数内にAPIを叩くCodeが無限に叩かれていることに気づきます。(console.logなどを書いて実際に試してください)

componentは純粋です。レンダリングに使用するsetup関数をトップレベルで使用しないでください。
> A component must be pure, You should not mutate any of the inputs that your components use for rendering. That includes props, state, and context. To update the screen, “set” state instead of mutating preexisting objects.


https://beta.reactjs.org/learn/keeping-components-pure



次に `useEffect`を使用してAPIを叩いた例を紹介します。
```react
import React from 'react';
import { useState, useEffect } from 'react';
const API_URL = 'https://jsonplaceholder.typicode.com/users';

export function App(props) {
  const [user, setUser] = useState({});

  useEffect(() => {
    fetch(API_URL)
      .then(res => res.json())
      .then(data => setUser(data[0]));
  }, []);

  return (
    <div className='App'>
      <ul>
        <li>{user.name}</li>
        <li>{user.email}</li>
      </ul>
    </div>
  );
}
```
 
useEffectは二つの引数を受け取ります。

```react
useEffect(setup, dependencies?)
```
第一引数はそのシステムに接続するsetup関数を渡します、これは特定のイベント(Click Hoberなど..)ではなくレンダリング自体によって引き起こされる副作用によってどのような挙動を起こしてほしいかをこの関数に書きます。

第二引数は依存配列を指定します。これを指定するとどの要素の変更によってレンダリングした際に発火するかを指定できます。指定しない場合は全てのレンダリングによって発火します。また、空文字を指定した際は最初の1回のレンダリングのみ発火します。

--- 
## クイズ

Q1. 次の`console.log('HelloWorld)`はいつ実行されるでしょうか?

1. 最初のレンダリング時のみ
2. ボタンを押した時のみ
3. 最初のレンダリング時とボタンを押した時
4. 実行されない 

```react
import React from 'react';
import { useState, useEffect } from 'react';

export function App(props) {
  const [count, setCount] = useState(0)
  
  useEffect(()=>{
    console.log('HelloWorld')
  },[count, setCount])

  return (
    <div className='App'>
      <button onClick={()=> setCount((prev) => prev + 1)}>押す</button>
    </div>
  );
}
```

<details><summary>正解をみる</summary>
正解は ３. 最初のレンダリング時とボタンを押した時
</details>

Q2. 次の`console.log('GoodNightWorld)`はいつ実行されるでしょうか?

```react
import React from 'react';
import { useState, useEffect } from 'react';

export function App(props) {
  const [count, setCount] = useState(0)

  useEffect(()=>{
    
    return () => console.log('GoodNightWorld')   
  },[count, setCount])

  return (
    <div className='App'>
      <button onClick={()=> setCount((prev) => prev + 1)}>押す</button>
    </div>
  );
}
```

1. 最初のレンダリング時のみ
2. ボタンを押した時のみ
3. 最初のレンダリング時とボタンを押した時
4. このコンポーネントがアンマウントされたタイミングのみ
5. このコンポーネントがアンマウントされたタイミングとボタンを押した時

<details><summary>正解をみる</summary>
正解は 5. このコンポーネントがアンマウントされたタイミングとボタンを押した時です。
</details>

`useEffect`の第一引数としてcallback関数を指定することでその依存関係の逆が実行された際に発火するようなものを作ることができます。

下記は外部のAPIにコネクションをComponent表示時(mount時)に行い、Componentから離れた場合(unmount時)にコネクションを切断しています.

```react
useEffect(() => {
  	const connection = createConnection(serverUrl, roomId);
    connection.connect();
  	return () => {
      connection.disconnect();
  	};
  }, [serverUrl, roomId]);
```

https://beta.reactjs.org/apis/react/useEffect#examples-dependencies


--- 

# useCallback

https://beta.reactjs.org/apis/react/useCallback

> useCallback is a React Hook that lets you cache a function definition between re-renders.

useCallbackは再レンダリング時に関数定義をキャッシュできるものです

## Reactが再レンダリングされる条件

1. コンポーネントのstateを更新(useStateのset関数を呼び出した時)
2. 親のコンポーネントが再レンダリングした時


```react
import React from 'react';
import { useState } from 'react';

const Children = () => {
  console.log('childrenさんだよ');
  return <div>I am Children</div>;
};

export function App(props) {
  const [text, setText] = useState('');

  return (
    <div className='App'>
      <input value={text} onChange={e => setText(e.target.value)} type='text' />
      <Children />
    </div>
  );
}
```


上記のCodeではinputに値が入るたびにChildren Componentのトップレベルで書かれているconsole.log が実行されています。

親コンポーネントの値の変更(今回だとinput要素の変更)は、子コンポーネントを表示する箇所に直接影響ないのに、再レンダリングされてしまっています。　reactではこのような不要なレンダリングを防ぐためにいくつか便利なReact HookやAPIが用意されています。 `useMemo` `useCallback` `memo` 
今回の例だと子コンポーネントを`memo`で囲むことによってレンダリングを回避することができます。

```react
const Children = memo(() => {
  console.log('childrenさんだよ');
  return <div>I am Children</div>;
});
```

## memoだけでは再レンダリングを防げないケース

memoだけでは不要なレンダリングを防げないケースがあります。
以下に例を示します。簡単な2倍, 3倍カウンターを表現したCodeです。
buttonをコンポーネント化し、onClick時に動作してほしい関数をそれぞれ渡しています。


```react
import React from 'react';
import {useState} from 'react'

const Button = React.memo(props => {
    const {label, onClick} = props;
    console.log('Button Componentがレンダリングされたよ')
    return <button onClick={onClick}>{label}</button>;
});

export const App = (props) => {
    // 2からスタートして2の倍数を管理するstate
    const [multipleTwo, setMultipleTwo] = useState(2);
    // 3からスタートして3の倍数を管理するstate
    const [multipleThree, setMultipleThree] = useState(3);

    // 2倍をセットする
    const clickMultipleTwo = () => {
        setMultipleTwo(multipleTwo * 2);
    };

    // 3倍をセットする
    const clickMultipleThree = () => {
        setMultipleThree(multipleThree * 3);
    };

    return (
        <div className='App'>
            <div>2の倍数: {multipleTwo}</div>
            <Button label='2倍' onClick={clickMultipleTwo}/>

            <div>3の倍数： {multipleThree}</div>
            <Button label='3倍' onClick={clickMultipleThree}/>
        </div>
    );
}

export default App
```

初回レンダリング時は以下のようにconsoleに2回表示されるはずです、これは初期のレンダリングなので正常な回数分レンダリングされています。
```
Button Componentがレンダリングされたよ
Button Componentがレンダリングされたよ
```

補足: 
もし、4回レンダリングされてしまった方はstrictModeをoffにしてください。

https://nextjs.org/docs/api-reference/next.config.js/react-strict-mode


https://ja.reactjs.org/docs/strict-mode.html


次に2倍のボタンを押して見ると
```
Button Componentがレンダリングされたよ
Button Componentがレンダリングされたよ
```

このように２回レンダリングが走ってしまうことがわかります。

```react
         <Button label='2倍' onClick={clickMultipleTwo}/>
```

本来はこの上記の部分が再レンダリングされるだけで良いはずです。

```react
        <Button label='3倍' onClick={clickMultipleThree}/>
```

しかし、実際にはこの部分も再レンダリングされていることがわかります。　


## なぜこのようなことが起こったのか

![スクリーンショット 2022-11-20 15.23.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1980538/d6faaeb1-e725-8e28-53a9-d9b726c5a808.png)

#### 1. clickされたので関数が実行される
#### 2. set関数によってApp(親)Componentが再レンダリングされる
#### 3. 再定義された二つの関数がButton Componentに渡される。Button ComponentではPropsの変更を検知して再レンダリングする。


つまり、今回は全く関係のなかった`clickMultipleThree`まで再定義されてしまったので子コンポーネントが再レンダリングされてしまったということです。

## useCallbackを使って再定義をキャッシュする

```react
import React, {useCallback} from 'react';
import {useState} from 'react'

const Button = React.memo(props => {
    const {label, onClick} = props;
    console.log('Button Componentがレンダリングされたよ')
    return <button onClick={onClick}>{label}</button>;
});

export const App = (props) => {
    // 2からスタートして2の倍数を管理するstate
    const [multipleTwo, setMultipleTwo] = useState(2);
    // 3からスタートして3の倍数を管理するstate
    const [multipleThree, setMultipleThree] = useState(3);

    // 2倍をセットする
    const clickMultipleTwo = useCallback(() => {
        setMultipleTwo(multipleTwo * 2);
    }, [multipleTwo])

    // 3倍をセットする
    const clickMultipleThree = useCallback(() => {
        setMultipleThree(multipleThree * 3);
    }, [multipleThree]);

    return (
        <div className='App'>
            <div>2の倍数: {multipleTwo}</div>
            <Button label='2倍' onClick={clickMultipleTwo}/>

            <div>3の倍数： {multipleThree}</div>
            <Button label='3倍' onClick={clickMultipleThree}/>
        </div>
    );
}

export default App
```

これによって2倍のボタンを押した際は2倍のボタンのみが再レンダリングの対象になるようになりました👏
```
const cachedFn = useCallback(fn, dependencies)
```
useCallbackは第一引数でwrapしたい関数を第二引数でuseEffectと同様依存関係を配列で渡します。

---

# まとめ

なんとなく使っていた調べていくうちにuseEffectやuseStateなどのReactHookについて詳しく理解することができました。また、Reactの公式ドキュメントはclass構文で書かれていて非常に読みにくいドキュメントでしたが、最近、ベータ版が出てきて非常にまとめられていて読みやすかったです。興味あったり、Reactを実務で使いこなしたい方は是非一度読むといいかもしれません


https://beta.reactjs.org/