기존 Context를 이용해서 State를 관리할 때는
CartItem이 바뀌면 useEffect훅을 이용해 CartTotal,CartCount도 바꿔줬다
{cartItems:[] , cartCount:0 , cartTotal:0}
useEffect가 아닌 Action을 이용한다
{type: STRING , payload: anything}
{type: “ADD_ITEM_TO_CART” , payload: {...cartItem}}
기본 형태
const [state, dispatch] = useReducer(reducer, initialState);
useState,useEffect로 state를 관리했던 cartContext를 useRedcuer로 migration하면서 사용법을 비교해보자
기존 CartContext
export const CartContext = createContext({
isCartOpen: false,
setIsCartOpen: () => {},
cartItems: [],
addItemToCart: () => {},
cartCount: 0,
removeItemFromCart: () => {},
clearItemFromCart: () => {},
cartTotal: 0,
});
export const CartProvider = ({ children }) => {
const [isCartOpen, setIsCartOpen] = useState(false);
const [cartItems, setCartItems] = useState([]);
const [cartCount, setCartCount] = useState(0);
const [cartTotal, setCartTotal] = useState(0);
useEffect(() => {
const newCount = cartItems.reduce(
(total, cartItem) => total + cartItem.quantity,
0
);
setCartCount(newCount);
}, [cartItems]);
useEffect(() => {
const newTotal = cartItems.reduce(
(total, cartItem) => total + cartItem.quantity * cartItem.price,
0
);
setCartTotal(newTotal);
}, [cartItems]);
const addItemToCart = (productToAdd) => {
setCartItems(addCartItem(cartItems, productToAdd));
};
const removeItemFromCart = (cartItemToRemove) => {
setCartItems(removeCartItem(cartItems, cartItemToRemove));
};
const clearItemFromCart = (cartItemToClear) => {
setCartItems(clearCartItem(cartItems, cartItemToClear));
};
const value = {
isCartOpen,
setIsCartOpen,
cartItems,
addItemToCart,
cartCount,
removeItemFromCart,
clearItemFromCart,
cartTotal,
};
return <CartContext.Provider value={value}>{children}</CartContext.Provider>;
};
cartImems가 바뀔때 cartCount와 cartTotal을 바꿔준다. =⇒ state변화가 자주 일어난다.
useReducer 사용후
export const CART_ACTION_TYPES = {
SET_CART_ITEM: 'SET_CART_ITEM',
SET_IS_CART_OPEN: 'SET_IS_CART_OPEN',
};
export const cartReducer = (state, action) => {
const { type, payload } = action;
switch (type) {
case CART_ACTION_TYPES.SET_CART_ITEM:
return {
...state,
...payload,
};
case CART_ACTION_TYPES.SET_IS_CART_OPEN:
return {
...state,
isCartOpen: payload,
};
default:
throw new Error(`Unhandled type ${type}`);
}
};
const INITIAL_STATE = {
isCartOpen: false,
cartItems: [],
cartCount: 0,
cartTotal: 0,
};
export const CartProvider = ({ children }) => {
const [{ cartItems, isCartOpen, cartTotal, cartCount }, dispatch] =
useReducer(cartReducer, INITIAL_STATE);
const updateCartItemsReducer = (newCartItems) => {
const newCount = cartItems.reduce(
(total, cartItem) => total + cartItem.quantity,
0
);
const newTotal = cartItems.reduce(
(total, cartItem) => total + cartItem.quantity * cartItem.price,
0
);
dispatch({
type: CART_ACTION_TYPES.SET_CART_ITEM,
payload: {
cartItems: newCartItems,
cartCount: newCount,
cartTotal: newTotal,
},
});
};
const addItemToCart = (productToAdd) => {
const newCartItems = addCartItem(cartItems, productToAdd);
updateCartItemsReducer(newCartItems);
};
const removeItemFromCart = (cartItemToRemove) => {
const newCartItems = removeCartItem(cartItems, cartItemToRemove);
updateCartItemsReducer(newCartItems);
};
const clearItemFromCart = (cartItemToClear) => {
const newCartItems = clearCartItem(cartItems, cartItemToClear);
updateCartItemsReducer(newCartItems);
};
const setIsCartOpen = (bool) => {
dispatch({ type: CART_ACTION_TYPES.SET_IS_CART_OPEN ,payload:bool});
};
const value = {
isCartOpen,
setIsCartOpen,
cartItems,
addItemToCart,
cartCount,
removeItemFromCart,
clearItemFromCart,
cartTotal,
};
return <CartContext.Provider value={value}>{children}</CartContext.Provider>;
};
ACTION_TYPES로 ACTION들을 정의해주고
cartReducer 함수는 기존state와 action객체로 새로운 state를 반환한다.
cartItem이 바뀔 때 모두 같은 updateCarrtItemsReducer함수 하나로 state를 업데이트 해준다.
setter함수를 한번에 여러번 호출할 구조가 생기면 useRedcuer를 사용하는것이 바람직해 보인다.