ReactのHooks入門

## **はじめに**

ReactのHooksは関数コンポーネントでの状態管理やライフサイクルの機能を使えるようにする、React 16.8で導入された機能です。

この記事では、Reactの主要なHooksを紹介し、それぞれの基本的な使い方を解説します。

## **Hooksとは**

Hooksは、クラスコンポーネントを書かずに、状態や他のReactの機能を関数コンポーネントで使えるようにする関数です。これにより、より読みやすく、再利用しやすいコンポーネントを作成できます。

## useStateについて

**`useState`** は、関数コンポーネントでstateを持たせるためのHookです。

これにより、以前はクラスコンポーネントでしか利用できなかったstateを、関数コンポーネントでも使うことができるようになります。

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

function Counter() {
  // useStateを使ってstate変数を宣言します
  // ここでは`count`というstate変数を0で初期化しています
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>You clicked {count} times</p>
      // setCountを使ってcountを更新します
      <button onClick={() => setCount(count + 1)}>
        Click me!
      </button>
    </div>
  );
}

export default function App() {
  return (
    <div className="App">
      <Counter />
    </div>
  );
}
```

この例では、**`useState`** を使って **`count`** という名前のstate変数を作成し、初期値として **`0`** を設定しています。
**`setCount`** はこの **`count`** を更新するための関数です。ボタンをクリックすると、 **`count`** が1増えるようになっています。

### 注意点

- **`useState`** は、コンポーネントのレンダリングごとに同じ順序で呼び出される必要があります。そのため、条件分岐やループの中で **`useState`** を呼び出すことはできません。
- **`useState`** に渡す初期値は、コンポーネントの最初のレンダリング時にのみ使用されます。それ以降のレンダリングでは無視されます。

## useEffectについて

**`useEffect`** は、コンポーネントがレンダリングされた後に何かしらの処理を行いたい場合に使用します。

これにより、クラスコンポーネントの **`componentDidMount`**、**`componentDidUpdate`**、**`componentWillUnmount`** に相当する処理を実装できます。

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

function Counter() {
  const [count, setCount] = useState(0);

  // カウントが更新された後に実行される
  useEffect(() => {
    document.title = `You clicked ${count} times`;
  }, [count]); // 依存配列にcountを指定

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me!
      </button>
    </div>
  );
}

export default function App() {
  return (
    <div className="App">
      <Counter />
    </div>
  );
}
```

**`useEffect`** は、第一引数に関数を、第二引数に依存配列（dependency array）を取ります。依存配列に指定された値が変更されたとき、またはコンポーネントがマウントされたとき（依存配列が空の場合）、第一引数の関数が実行されます。

### **依存配列の使用**

**`useEffect`** の第二引数に配列を渡すことで、配列内の値が変更された場合のみエフェクトが実行されるようになります。

```jsx
useEffect(() => {
  // APIを呼び出すなどの副作用を実行
}, [dependency]); // このエフェクトはdependencyが変更されたときのみ実行されます
```

## useContextについて

**`useContext`** はコンテキストオブジェクトを受け取り、そのコンテキストの現在値を返します。これにより、コンポーネントツリー内のどこからでもコンテキストを参照できるようになります。

```jsx
// ステップ1: ユーザー設定のためのコンテキストを作成
const UserSettingsContext = createContext();

// ステップ2: ユーザー設定を提供するプロバイダーコンポーネント
function UserSettingsProvider({ children }) {
  const userSettings = {
    username: 'Taro',
    theme: 'test'
  };

  return (
    <UserSettingsContext.Provider value={userSettings}>
      {children}
    </UserSettingsContext.Provider>
  );
}

// ユーザープロファイルコンポーネント
function UserProfile() {
  // ステップ3: useContextでユーザー設定を取得
  const settings = useContext(UserSettingsContext);

  return (
    <div>
      <h1>{settings.username}'s Profile</h1>
      <p>Current Theme: {settings.theme}</p>
    </div>
  );
}

export default function App() {
  return (
    <div className="App">
      <UserSettingsProvider>
        <UserProfile />
      </UserSettingsProvider>
    </div>
  );
}
```

- **ステップ1:** **`createContext()`** を使用して、新しいコンテキスト **`UserSettingsContext`** を作成します。これはユーザー設定を格納するためのものです。
- **ステップ2:** **`UserSettingsProvider`** というプロバイダーコンポーネントを作成し、**`UserSettingsContext.Provider`** を使用して、ユーザー設定の値をコンポーネントツリーに提供します。このコンポーネントはどの子コンポーネントも受け取ることができます。
- **ステップ3:** **`useContext(UserSettingsContext)`** を使用して、ユーザー設定の現在の値にアクセスします。これにより、**`UserProfile`** コンポーネント内でユーザー設定が直接使用できます。

**`useContext`** を使用することで、コンポーネントツリーを通じてデータを簡単に渡すことができます。これにより、propsのドリリング（親コンポーネントから子コンポーネントへと、多層にわたってpropsを渡していくプロセス）を避けることができ、コードの可読性と保守性を向上させることができます。

## useReducerについて

**`useReducer`** は、状態管理に関して **`useState`** よりも柔軟なアプローチを提供します。特に大規模なコンポーネントや複雑な状態ロジックを持つ場合に便利です。このフックを使うと、状態の更新ロジックをコンポーネントから分離し、よりテストしやすく、読みやすいコードを書くことができます。

### 基本的な使い方

**`useReducer`** は、現在の状態とそれを更新するためのアクションを引数に取る **`reducer`** 関数を使用します。これはReduxのreducerに似ていますが、Reduxを使うほどではない小規模なケースに適しています。

```jsx
import React, { useReducer } from 'react';

// 初期状態を定義
const initialState = { count: 0 };

// reducer関数を定義
// stateとactionを受け取り、新しい状態を返す
function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return { count: state.count + 1 };
    case 'decrement':
      return { count: state.count - 1 };
    default:
      throw new Error();
  }
}

function Counter() {
  // useReducerを使って状態とディスパッチ関数を取得
  const [state, dispatch] = useReducer(reducer, initialState);

  return (
    <>
      Count: {state.count}
      <button onClick={() => dispatch({ type: 'increment' })}>+</button>
      <button onClick={() => dispatch({ type: 'decrement' })}>-</button>
    </>
  );
}

export default function App() {
  return (
    <div className="App">
      <Counter />
    </div>
  );
}
```

この例では、**`Counter`** コンポーネント内で **`useReducer`** を使っています。

**`reducer`** 関数は、現在の状態とアクションオブジェクトを受け取り、新しい状態を返します。

このパターンはReduxで一般的ですが、ここではReactのコンテキスト内で完結しています。

## 最後に

ここまで読んでくださってありがとうございました。

ここで紹介できたのは一部のみですが、少しでもお役に立てば幸いです。

他にもHooksはたくさんありますので、気になった方はぜひ調べてみてください！

https://ja.react.dev/reference/react/hooks