MofuMofu2 / react-sandbox

React.js Sandbox
1 stars 0 forks source link

7-3 状態と処理とをまとめて管理する – useReducer関数 #68

Closed MofuMofu2 closed 10 months ago

MofuMofu2 commented 10 months ago

https://react.dev/reference/react/useReducer

MofuMofu2 commented 10 months ago

useStateとuseReducerの違いを説明してください。また、テストコードのサンプルがあれば知りたいです。

useStateuseReducer は React の状態管理フックですが、それぞれ異なる使用シナリオと利点があります。

useState

useReducer

テストコードのサンプル

useState のテスト

import { render, fireEvent } from '@testing-library/react';
import MyComponent from './MyComponent';

test('increments counter', () => {
  const { getByTestId } = render(<MyComponent />);
  fireEvent.click(getByTestId('increment-button'));
  expect(getByTestId('counter-value').textContent).toBe('1');
});

このテストでは、ボタンをクリックしてカウンターの値が増加するかを確認しています。

useReducer のテスト

import { render, fireEvent } from '@testing-library/react';
import MyReducerComponent from './MyReducerComponent';

test('increments counter through reducer', () => {
  const { getByTestId } = render(<MyReducerComponent />);
  fireEvent.click(getByTestId('increment-button'));
  expect(getByTestId('counter-value').textContent).toBe('1');
});

こちらのテストでは、useReducer を使用してカウンターの状態を管理し、同様にボタンクリックで状態が更新されることを確認しています。

これらのサンプルは基本的な例ですが、実際のコンポーネントやアプリケーションに合わせてテストをカスタマイズすることが重要です。

MofuMofu2 commented 10 months ago

複数のアクションに基づいて状態が更新される場合の一般的な例は、ショッピングカートの管理です。ショッピングカートでは、商品の追加、削除、数量の更新、カートのクリアなど複数のアクションが考えられます。これらのアクションをuseReducerを使って効率的に管理することができます。

ショッピングカートの例

State の構造

const initialState = {
  items: [], // カート内の商品
  totalAmount: 0, // 総額
  totalItems: 0, // 商品の総数
};

Reducer 関数

function cartReducer(state, action) {
  switch (action.type) {
    case 'ADD_ITEM':
      // 新しいアイテムを追加するロジック
      break;
    case 'REMOVE_ITEM':
      // アイテムを削除するロジック
      break;
    case 'UPDATE_QUANTITY':
      // アイテムの数量を更新するロジック
      break;
    case 'CLEAR_CART':
      // カートを空にするロジック
      return initialState;
    default:
      return state;
  }
}

コンポーネント内での使用

const [cartState, dispatch] = useReducer(cartReducer, initialState);

// アイテムを追加する関数
const addItem = item => {
  dispatch({ type: 'ADD_ITEM', payload: item });
};

// アイテムを削除する関数
const removeItem = itemId => {
  dispatch({ type: 'REMOVE_ITEM', payload: itemId });
};

// 数量を更新する関数
const updateQuantity = (itemId, newQuantity) => {
  dispatch({ type: 'UPDATE_QUANTITY', payload: { itemId, newQuantity } });
};

// カートをクリアする関数
const clearCart = () => {
  dispatch({ type: 'CLEAR_CART' });
};

この例では、各アクション(ADD_ITEM, REMOVE_ITEM, UPDATE_QUANTITY, CLEAR_CART)が特定の操作を表し、これらのアクションに応じてカートの状態が更新されます。useReducerを使用することで、これらの異なるアクションを一元管理し、状態の変更をより予測可能で管理しやすくなります。

MofuMofu2 commented 10 months ago

同じページで複数のフォームを扱う場合はuseReducerを使う方がいい、みたいなイメージでしょうか。

はい、その通りです。複数のフォームを同じページで扱う場合、useReducerを使用することで、フォームの状態管理をより簡潔に、かつ効率的に行うことができます。

useReducerを使用する利点

  1. 集中化された状態管理: 複数のフォームの状態を一つのリデューサー関数で管理できます。これにより、各フォームの状態が一箇所で更新され、状態の変更が追跡しやすくなります。

  2. 複雑な状態ロジックの簡素化: フォーム間での状態の共有や依存関係がある場合、useReducerはこれらの複雑なロジックをより簡単に扱うことができます。

  3. 再利用性とテスタビリティ: リデューサー関数は純粋関数であり、同じ入力に対して常に同じ出力を返すため、テストが容易になります。また、リデューサーを他のコンポーネントで再利用することも可能です。

実装の例

const formReducer = (state, action) => {
  switch (action.type) {
    case 'UPDATE_FIELD':
      return {
        ...state,
        [action.formName]: {
          ...state[action.formName],
          [action.fieldName]: action.value
        }
      };
    // 他のアクションハンドラ...
    default:
      return state;
  }
};

const MyComponent = () => {
  const [formState, dispatch] = useReducer(formReducer, {
    form1: { field1: '', field2: '' },
    form2: { field1: '', field2: '' },
    // 他のフォームの初期状態...
  });

  const handleChange = (formName, fieldName) => event => {
    dispatch({
      type: 'UPDATE_FIELD',
      formName,
      fieldName,
      value: event.target.value
    });
  };

  // フォームのレンダリングロジック...
};

この例では、formReducerは各フォームのフィールドを更新するためのアクション(UPDATE_FIELD)を処理します。これにより、異なるフォームの状態更新を一つの場所で処理することができ、コードの可読性と保守性が向上します。


複数の項目で成り立っているフォームなんかと相性が良さそうだなと思った。

MofuMofu2 commented 10 months ago

Reducer = State更新に利用する関数。Actionから更新する。この辺はVuexと似ている。 Actionはdispatch関数からでないと呼び出せない。

MofuMofu2 commented 10 months ago

reducerの条件

MofuMofu2 commented 10 months ago

テストの書き方を気を付ける必要はない。Stateとhandlerをくっつけて管理しているだけなので。その辺は楽。