ReactにおけるuseReducerについて

### useReducerとuseStateの比較

`useState`と`useReducer`は、Reactで状態管理を行うためのフックですが、それぞれ異なる用途と特性があります。

#### useState
- **用途**: シンプルな状態管理。状態が1つまたは少数の場合に適しています。
- **構文**:
  ```javascript
  const [state, setState] = useState(initialState);
  ```
- **状態更新**:
  - 状態を直接変更するための関数（`setState`）を使用します。
  - 更新は即時の状態を反映しない場合があるため、前の状態に依存する場合は関数形式で更新する必要があります。
  
- **例**:
  ```javascript
  const [count, setCount] = useState(0);
  
  const increment = () => setCount(count + 1); // 状態を直接変更
  // もしくは
  const increment = () => setCount(prevCount => prevCount + 1); // 前の状態に依存
  ```

#### useReducer
- **用途**: 複雑な状態管理や、状態の変更が多い場合に適しています。特に、状態がオブジェクトであったり、複数の状態が関連している場合に有効です。
- **構文**:
  ```javascript
  const [state, dispatch] = useReducer(reducer, initialState);
  ```
- **状態更新**:
  - 状態更新はアクションをディスパッチすることで行います。アクションにはタイプと必要なデータが含まれます。
  - `reducer`関数が状態とアクションを受け取り、新しい状態を返します。

- **例**:
  ```javascript
  const initialState = { count: 0 };
  
  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();
    }
  }
  
  const [state, dispatch] = useReducer(reducer, initialState);
  
  const increment = () => dispatch({ type: 'increment' }); // アクションをディスパッチ
  ```

### useReducerの実装パターン別の実装例

以下に、`useReducer`を使った異なる実装パターンの雛形を示します。

#### 1. **基本的なカウンター**
```javascript
import React, { useReducer } from 'react';

const initialState = { count: 0 };

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();
  }
}

export default function Counter() {
  const [state, dispatch] = useReducer(reducer, initialState);

  return (
    <div>
      <p>Count: {state.count}</p>
      <button onClick={() => dispatch({ type: 'increment' })}>Increment</button>
      <button onClick={() => dispatch({ type: 'decrement' })}>Decrement</button>
    </div>
  );
}
```

#### 2. **複数の状態を持つ**
```javascript
import React, { useReducer } from 'react';

const initialState = { count: 0, text: '' };

function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return { ...state, count: state.count + 1 };
    case 'setText':
      return { ...state, text: action.text };
    default:
      throw new Error();
  }
}

export default function MultiState() {
  const [state, dispatch] = useReducer(reducer, initialState);

  return (
    <div>
      <p>Count: {state.count}</p>
      <input
        type="text"
        value={state.text}
        onChange={e => dispatch({ type: 'setText', text: e.target.value })}
      />
      <button onClick={() => dispatch({ type: 'increment' })}>Increment</button>
    </div>
  );
}
```

#### 3. **APIからのデータ取得**
```javascript
import React, { useReducer, useEffect } from 'react';

const initialState = { loading: true, data: null, error: null };

function reducer(state, action) {
  switch (action.type) {
    case 'fetchSuccess':
      return { loading: false, data: action.data, error: null };
    case 'fetchError':
      return { loading: false, data: null, error: action.error };
    default:
      throw new Error();
  }
}

export default function FetchDataComponent() {
  const [state, dispatch] = useReducer(reducer, initialState);

  useEffect(() => {
    const fetchData = async () => {
      try {
        const response = await fetch('https://api.example.com/data');
        const data = await response.json();
        dispatch({ type: 'fetchSuccess', data });
      } catch (error) {
        dispatch({ type: 'fetchError', error: error.message });
      }
    };

    fetchData();
  }, []);

  if (state.loading) return <p>Loading...</p>;
  if (state.error) return <p>Error: {state.error}</p>;
  
  return (
    <div>
      <h1>Data:</h1>
      <pre>{JSON.stringify(state.data, null, 2)}</pre>
    </div>
  );
}
```