Reactのカスタムフック

# 超有益資料
まず初めにこちらの資料は非常にわかりやすいです。ぜひ読んでみてください！

https://read-engineer.com/2020/12/11/react/#index_id5

https://qiita.com/seira/items/e62890f11e91f6b9653f

# 有益Udemy
かなりいろんな内容が網羅されています。

https://www.udemy.com/course/react-complete-guide/


# カスタムフックとは
まずこれはUseStateなどのReact Hookを内部で使う***関数***です。
***hocks(useState,useEffect)などで使えます***。
これは***コンポーネントからロジックを分けていくもの***です。
複数のコンポーネントで同じロジックを使いたいときや同じロジックが複数のコンポーネントで使われている時に、そのロジックをカスタムフックにまとめることでコードの重複を避け、再利用性が上がります。

***React Hookを関数に切り出して再利用***できます。

### 具体的にどんな時に使う？
ex)
APIからデータを取得して表示する処理が複数の場所で必要な場合などです。


# カスタムフック使い方
ここはChatGPIを参照します。

カスタムフックを作成するには、以下の手順に従います。

|順番|説明|
|--|--|
|1|関数を作成する：状態や副作用を扱うための関数を作成します。|
|2|フックを使用するコンポーネントでimportする：カスタムフックを使用したいコンポーネントで、フックをimportします。|
|3|フックを使用する：コンポーネント内で、フックを使用して関数を呼び出し、戻り値を使用します。|

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

function useCounter(initialCount) {
  const [count, setCount] = useState(initialCount);

  useEffect(() => {
    document.title = `Count: ${count}`;
  }, [count]);
  function increment() {
    setCount(count + 1);
  }

  function decrement() {
    setCount(count - 1);
  }

  return { count, increment, decrement };
}
```
このカスタムフックは、状態変数 count と、 increment および decrement 関数を返します。また、 useEffect フックを使用して、 count の値が変更された場合にドキュメントのタイトルを更新します。
これを使用する場合、以下のようになります。

```jsx
import { useCounter } from './useCounter';

function Counter() {
  const { count, increment, decrement } = useCounter(0);

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={increment}>Increment</button>
      <button onClick={decrement}>Decrement</button>
    </div>
  );
}
```
```jsx
import { useCounter } from './useCounter';

function Counter() {
  const { count, increment, decrement } = useCounter(0);

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={increment}>Increment</button>
      <button onClick={decrement}>Decrement</button>
    </div>
  );
}
```
このコンポーネントは、 useCounter フックを使用して、状態変数 count と、 increment および decrement 関数を取得し、それらを使用してカウンターを実装しています。

以上が、Reactのカスタムフックの使い方になります。


# useEffectを使う
関数の実行タイミングをReactのレンダリング後まで遅らせるhookです。
副作用の処理を関数コンポーネントで扱えます。
副作用とは、データの取得、DOMの更新、アニメーションの開始、イベントリスナーの追加など、コンポーネントのレンダリングに直接関係のない処理です。

```Practice.jsx
import { useState,useEffect } from "react";

const Practice = () => {
  const [time,setTime] = useState(0);//useState フックを使用して、 time という状態変数を宣言し、初期値を 0
  useEffect(()=>{
    window.setInterval(()=>{
      setTime(prev => prev+1);
    },1000);
  },[])//seEffect フックに渡された第2引数が空の配列 [] であるため、 useEffect フックは、コンポーネントが初めてマウントされたときに一度だけ実行
  return(
    <h2>
    <time>{time}</time>
    <span>秒経過</span>
    </h2>
  );
};

export default Practice;
```
```useEffect```を使って、コンポーネントが初めてマウントされたときに一度だけ実行されるタイマーをセットアップします。このタイマーは、1秒ごとにtimeの状態を更新することで、経過時間を測ります。

![スクリーンショット 2023-04-24 15.19.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1748789/97e05c6f-619f-9abf-0f61-f14c5178ef80.png)

スタートなどのボタンはここでは紹介していません。

# useEffectの依存配列の使い方
さっきの[]の中にtimeを入れました。
配列に含めたステートが更新されるとコールバック関数が再度実行されます。
***[]の場合は依存しているステートがないので、コールバック関数はPracticeコンポーネントが実行された時に実行***されます。

```jsx
const Example = () => {
  const [time,setTime] = useState(0);
  useEffect(()=>{
    console.log('useEffect is called');
    window.setInterval(()=>{
      setTime(prev => prev+1);
    },1000);
  },[])

useEffect(() => {
  console.log('updated');
},[time])

//useEffect(() => {
//  document.title = 'counter:'+ time;
 // window.localStorage.setItem('time-key',time);
//},[time])
//余力あれば実装
```
### 注意
useEffectが依存している値ですが、コールバック関数の中で更新してはいけないみたいです。
上ではtime。

```jsx
useEffect(() => {
  document.title = 'counter:'+ time;
  window.localStorage.setItem('time-key',time);
  setTime(prev => prev+1);//これはだめ！無限ループになる
},[time])
```
# useEffectのクリーンアップ処理
クリーンアップ処理は、useEffectで設定した副作用が不要になったときに実行される処理です。


```jsx
import React, { useEffect } from "react";

const Practice = () => {
  useEffect(() => {
    // マウント時の処理

    // クリーンアップ処理
    return () => {
      // アンマウント時の処理
    };
  }, []);

  return <div>コメント</div>;
};

export default Practice;
```
Reactコンポーネントのクリーンアップ処理です。トグルボタンを押すとボタンが非表示になります。
そして経過時間も表示されます。
```jsx
import React, { useEffect, useState } from "react";

const Example = () => {
  const [isDisp, setIsDisp] = useState(true);

  return (
    <>
      {isDisp && <Timer />}
      <button onClick={() => setIsDisp((prev) => !prev)}>トグル</button>
    </>
  );
};

const Timer = () => {
  const [time, setTime] = useState(0);

  useEffect(() => {
    const intervalId = window.setInterval(() => {
      setTime((prev) => prev + 1);
    }, 1000);

    return () => {
      clearInterval(intervalId); // インターバルをクリアする
    };
  }, []);

  useEffect(() => {
    document.title = "counter:" + time;
    window.localStorage.setItem("time-key-end", time);
  }, [time]);

  return (
    <h3>
      <time>{time}</time>
      <span>秒経過</span>
    </h3>
  );
};

export default Example;
```

# useLayoutEffect
DOMの変更後に非同期に実行されるため、DOMの変更が画面に反映された後に副作用が発生するのが```useEffect```で、DOMの変更直後に同期的に実行されるため、DOMの変更前に副作用が発生するのが```useLayoutEffect```です。

1. DOMの変更を監視して、その変更に基づいて計算や処理を行う場合。
2. DOMの変更前に特定の処理（例: スクロール位置の取得）を行いたい場合。

> 引用　ChatGptより引用

```jsx
import { useEffect,useLayoutEffect, useState } from "react";

const Practice = () => {
  const [isDisp, setIsDisp] = useState(true);

  return (
    <>
      {isDisp && <Timer/>}
      <button onClick={() => setIsDisp(prev => !prev)}>トグル</button>
    </>
  )
}
const Timer = () => {
  const [time, setTime] = useState(0);

  useEffect(() => {
    // console.log('init');
    let intervalId = null;
    intervalId = window.setInterval(() => {
      console.log('interval called');
      setTime(prev => prev + 1);
    }, 1000);
    return () => {
      window.clearInterval(intervalId)
      // console.log('end');
    }
  }, [])
  
  useEffect(() => {
    // console.log('updated');
    
    document.title = 'counter:' + time;
    window.localStorage.setItem('time-key', time);

    return () => {
      // console.log('updated end');
    }
  }, [time]);

  useLayoutEffect(()=>{
    const _time = parseInt(window.localStorage.getItem
    ('time-key'));
    if(!isNaN(_time)){
      setTime(_time);
    }
  },[])

  return (
    <>
    <h3>
      <time>{time}</time>
      <span>秒経過</span>
    </h3>
    <Random />
    </>
    );
};
export default Practice;
```


# 関数型プログラミングで大事なこと
1.***値の状態と処理は切り離す***
2.***特定の入力には特定の出力を返します(副作用を排除する純粋関数)***
3.***一度設定した値は書き換えない***



# 資料

https://read-engineer.com/2020/12/11/react/#index_id5

https://www.udemy.com/course/react-complete-guide/

ChatGptを使用