Hook(useState, useEffect) [5日間でReactの基礎を習得したい 3/5日目]

# はじめに

5日間でReactの基礎を習得する試みの3日目です。3日目は、フックについて学びます。

https://qiita.com/MichaelHashimoto/items/f3ad9f56081a40b9b788

私はC#やPythonを仕事で使っているため、オブジェクト指向の言語的な考え方や、Pythonでの例えを使って理解を進めます。同じような境遇の方の理解の助けになれば幸いです。

# フック(hook)

クラスコンポーネントでは出来るが、関数コンポーネントでは出来ないような実装があります。
例えば、クラスコンポーネントはメンバ変数に値を保持しておくことができます。しかし、当然ながら関数コンポーネントではstateなどのメンバ変数を保持することができません。

## そこでフックを使う

メンバ変数等が必要でクラスコンポーネントでしかできなかった実装を、関数コンポーネントできるようにしてくれるのがフックです。

ここでは特にメジャーなuseStateとuseEffectを扱います。

## useState

useStateは関数コンポーネントで、stateを使うためのフックです。(stateは[2日目](https://qiita.com/MichaelHashimoto/items/f4779c8d7f9338d61646#props%E3%81%A8state)に扱いました。)

### 使ってみる

[1日目](https://qiita.com/MichaelHashimoto/items/b813096be60e63ba2816#nextjs%E3%81%AE%E6%96%B0%E8%A6%8F%E3%83%97%E3%83%AD%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E3%81%AE%E4%BD%9C%E6%88%90)に作ったプロジェクトのsrc/app/page.jsには、Homeコンポーネントが定義されています。
この中身を全部消して、以下のように編集しましょう。
```jsx:page.js
"use client";

import React, { useState } from 'react';

// ボタンをクリックすると、表示される値がインクリメントされるコンポーネント
export default function Home() {
  // useStateを使用してカウンターの状態を初期化
  const [clickedCount, setClickedCount] = useState(0);

  // カウンターをインクリメントする関数
  const incrementCount = () => {
    setClickedCount(clickedCount + 1);
  };

  return (
    <div>
      <h1>Welcome to Next.js!</h1>
      <p>クリック数のカウント: {clickedCount}</p>
      <button onClick={incrementCount}>Increment Counter</button>
    </div>
  );
}
```

```npm run dev```で起動すると、画面が表示されます。
"Increment Counter"ボタンを4回クリックすると次のような画面になります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1223968/d7301cb2-836d-c9fb-c5ea-05ab6d0d51ab.png)


### 解説
useStateは ```const [stateを格納する変数名, stateを更新する関数名] = useState(stateの初期値);```で宣言します。
```jsx
const [clickedCount, setClickedCount] = useState(0);  // clickedCountの初期値は0になる
```

ボタンをクリックしたときのイベントにincrementCount関数を設定しています。この関数内でsetClickedCount(clickedCountの値を更新する関数)を実行しています。
```jsx
// カウンターをインクリメントする関数
const incrementCount = () => {
    setClickedCount(clickedCount + 1);
};
```

incrementCountが実行されると、clickedCountの値が変更されるため、再描画されて"クリック数のカウント: "の数値が更新されます。
```jsx
// clickedCountの値が変わると、画面が再描画されて値が更新される
<p>クリック数のカウント: {clickedCount}</p>  
```

## useEffect

useEffectは、関数コンポーネントでコンポーネントが描画された後に副作用を実行するためのフックです。

副作用とは、コンポーネントのUIを変えるような処理・外部に対して操作する処理のことを指します。具体的な内容は[こちら](https://qiita.com/Mitsuw0/items/801f783ca74b062c1ed8#react%E3%81%AB%E3%81%8A%E3%81%91%E3%82%8B%E5%89%AF%E4%BD%9C%E7%94%A8) の記事が参考になります。

<details><summary>useEffectのクラスコンポーネントからの理解</summary>
副作用は、コンポーネントがすべて表示(マウント)されたあとに実行します。
クラスコンポーネントを使えば、コンポーネントが描画されたときの初期処理、コンポーネントが更新された後の処理などに、任意の処理を実行させられます。これは、各処理に相当するメソッドがクラスコンポーネントが継承しているReact.Copmponentに実装されており、それをoverrideすれば実装できます。

```jsx
// これは模擬的な React.Component クラス。この感じのものをクラスコンポーネントは継承している。
class React.Component {
  constructor(props) {
    this.props = props;
  }

  componentDidMount() {
    // コンポーネントマウント後の初期処理
  }

  shouldComponentUpdate(nextProps, nextState) {
    // プロップス/ステート変更時の再レンダリング判断
    return true;
  }

  componentDidUpdate(prevProps, prevState) {
    // コンポーネント更新後の処理
  }

  componentWillUnmount() {
    // コンポーネントアンマウント前のクリーンアップ
  }

  render() {
    // UIレンダリングのロジックを実装必須
    throw new Error("Render method must be implemented");
  }
}
```

しかし、関数コンポーネントでは、このようなoverrideはできません。そこで、useEffectフックを使います。
</details>



## 使ってみる

Homeコンポーネントを以下のように編集しましょう。
```jsx:page.js
"use client";

import React, { useEffect } from 'react';

// マウント(画面表示)完了後にコンソールにログが出力されるコンポーネント
export default function Home() {
  // コンポーネントがマウントされた後の副作用
  useEffect(() => {
    console.log('Component is mounted.');
  }, []);

  return (
    <div>
      <h1>Welcome to Next.js!</h1>
      <p>コンソールをチェックしてください</p>
    </div>
  );
}
```

```npm run dev```で起動したあと、ブラウザで表示してF12の開発者ツールにてコンソールを確認してください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1223968/e90d88e1-8365-d2d4-16a7-c24fac0594f3.png)


https://qiita.com/kim_t0814/items/ce95b6b92e7994d4fdac#react18%E3%81%AE%E6%8C%99%E5%8B%95 この記事によると、開発モードで実行すると、2回マウントされるようなので、コンソールにも2回分のログが出ています。

### 解説

useEffectは、```useEffect(実行する関数, 監視する値のリスト[省略可])```で宣言します。
2引数目の"監視する値のリスト"ですが、このリストに値を入れておくと、その値が変わるたびに、関数が実行されます。何も書かない場合（[]とする場合）、コンポーネントがマウントされる時だけコードが実行されます。
```jsx
useEffect(() => {
    console.log('Component is mounted.');
}, []); // 今回はマウント時だけ実行されることになる
```

# 最後に

フックは他にも沢山ありますし、自作もできるようです。ただし、大事なことは"クラスコンポーネントでしかできなかった実装を関数コンポーネントでもできるようにする"という基本を覚えておくことだと感じました。
複雑な場合は、クラスコンポーネントだったらどう実装するのか、という観点から理解するのがよさそうです。

次回は既存のリポジトリを編集し、実践的に学びます。

何か間違いがあれば、是非コメントを頂けると助かります！