Reactを基本からまとめてみた【9】【React Hook (useEffect)】

## useEffectとは
関数コンポーネント内で副作用を実行するためのフック。
例：APIを用いたデータ取得。購読(subscription処理)の設定。⇨ 誰か（フォローしている人）が呟き投稿した際に自身のタイムラインにリアルタイムでデータが反映される処理

## 副作用(side effect)とは

- UI(（ユーザーインターフェース）)の構築意外の処理
- 関数コンポーネントはではuseEffectという副作用フックを使う
- 副作用＝レンダリングによって引き起こされる処理

######※ UIとは、ユーザーインターフェイス（User Interface）の略称で、一般的にユーザー（利用者）と製品やサービスとのインターフェース（接点）全てのことを意味する。

## 第二引数の依存関係を理解する

- useEffectの第二引数には配列を渡すことが可能
- 第二引数はdeps(dependencies)と呼ばれ、副作用が引き起こされるかどうかの依存
関係となる

```sample.js
// 毎回実行される
useEffect(() => {
 console.log('Current count is...', count)
})

// 初回レンダリング後のみ実行される
useEffect(() => {
 console.log('Current count is...', count)
}, [])

// triggerが変更される度に実行される
useEffect(() => {
 console.log('Current count is...', count)
}, [trigger])

// trigger1かtrigger2が変更される度に実行される
useEffect(() => {
 console.log('Current count is...', count)
}, [trigger1,trigger2])

```


## コードの書き方は大きく２種類
####クリーンアップ関数とは
コンポーネントがアンマウントされる直前に呼び出される関数のこと。
クリーンアップ関数を用意することでメモリリークを防ぐ事ができる。

#### メモリリークとは
確保したメモリ領域の解放処理の忘れが原因で、メモリの空き容量が減っていくこと。
メモリの容量が減っていくと他の処理でそのメモリを使う事ができなくなるので、PCの処理速度が低下するなどの不都合が生じる。

#### (1)クリーンアップ関数を必要としない場合
#### (2)クリーンアップ関数を必要とする場合

## 実際にコーディングしてみる
### 1. Countアプリを通して基本的なuseEffectの動きを理解
※Reactを基本からまとめてみた【8】【React Hook (useState)】のCountアプリを使用

```App.tsx
import React from 'react';
import Counter from './components/Counter';
// import InputForm from './components/InputForm';

function App() {
  return (
    <div className="App">
      <h2>#2 useEffect</h2>
      <Counter />
      {/* <InputForm /> */}
    </div>
  );
}
export default App;
```

```Counter.tsx
import React, { useState } from 'react';

const Counter: React.VFC = () => {
    const [count, setCount] = useState(0);

    return (
    <button 
      onClick={() => {
        setCount((prevCount) => prevCount + 1);
        setCount((prevCount) => prevCount + 1);
     }}
    >
    {count}
  </button>
  );
};
export default Counter;
```

(1)初めてのuseEffect

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

const Counter: React.VFC = () => {
    const [count, setCount] = useState(0);

    useEffect(() => {
      console.log('初めてのuseEffect');
    });

    return (
    <button 
      onClick={() => {
        setCount((prevCount) => prevCount + 1);
        setCount((prevCount) => prevCount + 1);
     }}
    >
    {count}
  </button>
  );
};
export default Counter;
```

(2)マウント時のみuseEffect内の処理を行いたい場合

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

const Counter: React.VFC = () => {
    const [count, setCount] = useState(0);

    useEffect(() => {
      console.log('初めてのuseEffect');
    },[]);

    return (
    <button 
      onClick={() => {
        setCount((prevCount) => prevCount + 1);
        setCount((prevCount) => prevCount + 1);
     }}
    >
    {count}
  </button>
  );
};
export default Counter;
```

(3)マウント時 + 特定のstateが変化した際にuseEffectないの処理を行いたい場合

src / counter / Counter.js作成する。

``` Counter.js
import React, { useState, useEffect } from 'react';
import classes from './Counter.module.scss';

const Counter: React.VFC = () => {
  
    const [count1, setCount1] = useState(0);
    const [count2, setCount2] = useState(0);

    useEffect(() => {
      console.log('マウント時とcount1が変化した時にuseEffectが発火');
    },[count1]);

    return (
      <div className={classes.root}>
        <div className={classes.buttonWrapper}>
          <span className={classes.buttonText}>カウント１</span>
    <button 
      onClick={() => {
        setCount1((prevCount) => prevCount + 1);
     }}
    >
    {count1}
        </button>
        </div>
        <div className={classes.buttonWrapper}>
          <span>className={classes.buttonText}カウント2</span>
  <button
    onClick={() => {
        setCount2((prevCount) => prevCount + 1);
     }}
     >
     {count2}
          </button>
          </div>
  </div>
  );
};
export default Counter;
```
(4)クリーンアップ関数が必要になるケース

```App.tsx
import React, {useState} from 'react';
import Counter from './counter/Counter';
// import InputForm from './components/InputForm';

function App() {
  const [isDisplay, setIsDisplay] = useState(true);
  const handleDisplay = () => {
    setIsDisplay(!isDisplay);
  };
  return (
    <div className="App">
      <h2>#2 useEffect</h2>
      <button　onClick={() => handleDisplay()}>
        {isDisplay ? 'コンポーネント非表示'　: 'コンポーネント表示'}
      </button>
      {isDisplay && <Counter />}
      {/* <InputForm /> */}
    </div>
  );
}
export default App;
```

```Counter.tsx
import React, { useState, useEffect } from 'react';
import classes from './Counter.module.scss';

const Counter: React.VFC = () => {
    const [count1, setCount1] = useState(0);
    const [count2, setCount2] = useState(0);

    useEffect(() => {
      console.log('マウント時とcount1が変化した時にuseEffectが発火');
      const countUp = setInterval(() => {
        setCount1((prevCount) => prevCount + 1);
        console.log('countUp');
      }, 1000);
      return () => {
        clearInterval(countUp);
        console.log('unMount');
      }
    },[]);

    return (
      <div className={classes.root}>
        <div className={classes.buttonWrapper}>
          <span className={classes.buttonText}>カウント１</span>
    <button 
      onClick={() => {
        setCount1((prevCount) => prevCount + 1);
        // setCount((prevCount) => prevCount + 1);
     }}
    >
    {count1}
        </button>
        </div>
        <div className={classes.buttonWrapper}>
          <span>className={classes.buttonText}カウント2</span>
  <button
    onClick={() => {
        setCount2((prevCount) => prevCount + 1);
        // setCount((prevCount) => prevCount + 1);
     }}
     >
     {count2}
          </button>
          </div>
  </div>
  );
};
export default Counter;
```


###2. useEffectを用いて外部からデータを取得し画面に表示
(1)axiosを導入しデータを取得できているか動作確認する
※ {JSON}palaceholderを使用する

```
terminal
yarn add axios
```
components / itemList / itemList.tsxファイルを作成する。

(2)mapを用いて取得したデータを表示する

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

type TodoType = {
    userId: number;
    id: number;
    title: string;
    completed: boolean;
};
type PostType = {
    userId: number;
    id: number;
    title: string;
    body: string;
};
type ItemType = {
    id: number;
    title: string;
};
type ResourceType = 'todos'|'posts';

const itemList: React.VFC = () => {
    const [items, setItems] = useState<ItemType[]>([]);
    const [resource, setResource] = useState<ResourceType>('todos');

    useEffect(() => {
        const fetchItems = async () => {
            try {
                const response = await axios.get(
                    'https://jsonplaceholder.typicode.com/${resource}'
                );
                console.log(response);
                const itemData: ItemType[] = response.data.map(
                    (responseData: TodoType | PostType) => {
                        return { id: responseData.id, title: responseData.title };
                    }
                );
                setItems(itemData);
            } catch (err) {
                console.error(err);
            }
        };
        fetchItems();
    }, [resource]);

    const onTodoButtonClick = () => {
        setResource('todos');
        //console.log('Todos')
    };
    const onPostButtonClick = () => {
         setResource('posts');
        // console.log('Posts')
    };
    return (
        <div>
            <button onClick={()=> onTodoButtonClick()}>Todos</button>
            <button onClick={() => onPostButtonClick()}>Posts</button>
            <ul>
                {items.map((item) => (
                    <li key={item.id}>{item.title}</li>
                ))}
            </ul>   
        </div>
    );
};

export default itemList;
```




## 参考サイト
[【React Hooks】useEffectの基本的な動きを理解して使いこなそう](https://tyotto-good.com/blog/basic-useeffect)
[【ReactHooks入門】第3回-1：useEffectの理解前編](https://www.youtube.com/watch?v=McEbOBmtvYs)
[【ReactHooks入門】第3回-2：useEffectの理解後編](https://www.youtube.com/watch?v=ivi5fv22Erw)
[基礎編】ライフサイクルと副作用を理解しよう](https://www.youtube.com/watch?v=f8iXdpbvvH8)