状態保存管理を簡単にやすくする方法(useContext + useReducer)

概要

Reactで状態保存とするとuseState, useContext, Redux、Recoilなどがあるが

プロジェクトの規模によってuseStateでは足りない、Redux, Recoilなどでもないと思ったら

useContextが良いと思う。だけと、保存だけでは意味がないのでuseReducerと合わせて管理することを考えられる。ここでuseContextでプロジェクト全般でアクセスできる、useReducerで状態を管理することを考慮しても良いかと思う。

構造

1.初期状態を定義

const initialState = {
  ....
}

2.管理(追加、更新、削除)アクションの定義

const cartReducer = (state, action) => {
  switch(action.type) { case 'ADD_ACTION': ... ... }
}

3. 状態保存(context)を生成

const CartContext = createContext({
  ... ...
})

4. 供給者(provider)を定義

App.jsやindex.jsでProviderで囲んでは以下のコンポネントで利用する。

const CartProvider = ({ children }) => {
  // useReducerでdispatcherを定義
  // アクションを呼び出しを用意
 // 状態保存を用意
  // Providerを返す
}

5. カスタムフックを定義

const useCart = () => useContext(CartContext);

実装例(一部のみ)

1. CartContext <-これが重要

// CartContext.js

import { createContext, useReducer, useContext } from 'react';

// 初期状態
const initialState = {
  items: [],
  totalAmount: 0,
};

// 管理(追加、更新、削除)アクションの定義
const cartReducer = (state, action) => {
  switch (action.type) {
    case 'ADD_ITEM':
      // ... 詳細は省略 ...

      return {
        items: updatedItems,
        totalAmount: updatedTotalAmount,
      };

    case 'REMOVE_ITEM':
            // ... 詳細は省略 ...

      return {
        items: removedUpdatedItems,
        totalAmount: updatedTotalAmount,
      };
    default:
      return initialState;
  }
};

// Context生成
const CartContext = createContext({
  items: [],
  totalAmount: 0,
  addItem: (item) => {},
  removeItem: (id) => {},
});

// 供給者(provider)を定義
export const CartProvider = ({ children }) => {
  const [cartState, dispatchCartAction] = useReducer(cartReducer, initialState);

  const addItemHandler = (item) => {
    dispatchCartAction({ type: 'ADD_ITEM', item: item });
  };

  const removeItemHandler = (id) => {
    dispatchCartAction({ type: 'REMOVE_ITEM', id: id });
  };

  const cartContext = {
    items: cartState.items,
    totalAmount: cartState.totalAmount,
    addItem: addItemHandler,
    removeItem: removeItemHandler,
  };

  return (
    <CartContext.Provider value={cartContext}>
      {children}
    </CartContext.Provider>
  );
};

// カスタムフックを定義。
export const useCart = () => useContext(CartContext);

2.App.js


function App() {

  return (
    <CartProvider>
      <Products />
      <Cart />
    </CartProvider>
    </>
  );
}

3. Product.js <- アクションなど!

const Products = () => {
  const { addItem } = useCart();

  const handleAddToCart = (product) => {
    addItem({ ...product, amount: 1 });
  };

  return (
    // ... 省略 ..
            <button onClick={() => handleAddToCart(product)}>
              カートへ追加
            </button>
    // ... 省略 ..
  )
}

4. Cart.js

const Cart = () => {
  const { items, totalAmount, removeItem } = useCart();

  const handleRemoveFromCart = (id) => {
    removeItem(id);
  };

  return (
    <div>
      <h2>カート</h2>
      // ... 省略 ...(cartのitemの一覧表示、削除ボタン表示)
    </div>
  )
}

タイトルとURLをコピーしました