export const ADD_TO_CART = "ADD_TO_CART";
export const REMOVE_FROM_CART = "REMOVE_FROM_CART";
export const SET_QUANTITY = "SET_QUANTITY";
export const NOTIFY = "NOTIFY";
export const ENQUEUE_NOTIFICATION = "ENQUEUE_NOTIFICATION";
export const DEQUEUE_NOTIFICATION = "DEQUEUE_NOTIFICATION";
// actions creator functions
export const addToCart = (itemId) => {
return {
type: ADD_TO_CART,
payload: {
quantity: 1,
itemId,
},
};
};
export const removeFromCart = (itemId) => {
return {
//TODO
type: REMOVE_FROM_CART,
payload: { itemId },
};
};
export const setQuantity = (itemId, quantity) => {
return {
//TODO
type: SET_QUANTITY,
payload: {
quantity,
itemId,
},
};
};
export const notify =
(message, dismissTime = 5000) =>
(dispatch) => {
const uuid = Math.random();
dispatch(enqueueNotification(message, dismissTime, uuid));
setTimeout(() => {
dispatch(dequeueNotification());
}, dismissTime);
};
export const enqueueNotification = (message, dismissTime, uuid) => {
return {
type: ENQUEUE_NOTIFICATION,
payload: {
message,
dismissTime,
uuid,
},
};
};
export const dequeueNotification = () => {
return {
type: DEQUEUE_NOTIFICATION,
};
};
import React, { useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import { removeFromCart, setQuantity } from "../actions";
import CartItem from "../components/CartItem";
import OrderSummary from "../components/OrderSummary";
export default function ShoppingCart() {
const state = useSelector((state) => state.itemReducer);
const { cartItems, items } = state;
const dispatch = useDispatch();
const [checkedItems, setCheckedItems] = useState(
cartItems.map((el) => el.itemId)
);
const handleCheckChange = (checked, id) => {
if (checked) {
setCheckedItems([...checkedItems, id]);
} else {
setCheckedItems(checkedItems.filter((el) => el !== id));
}
};
const handleAllCheck = (checked) => {
if (checked) {
setCheckedItems(cartItems.map((el) => el.itemId));
} else {
setCheckedItems([]);
}
};
const handleQuantityChange = (quantity, itemId) => {
//TODO: dispatch 함수를 호출하여 액션을 전달하세요.
dispatch(setQuantity(itemId, quantity));
};
const handleDelete = (itemId) => {
setCheckedItems(checkedItems.filter((el) => el !== itemId));
//TODO: dispatch 함수를 호출하여 액션을 전달하세요.
dispatch(removeFromCart(itemId));
};
const getTotal = () => {
let cartIdArr = cartItems.map((el) => el.itemId);
let total = {
price: 0,
quantity: 0,
};
for (let i = 0; i < cartIdArr.length; i++) {
if (checkedItems.indexOf(cartIdArr[i]) > -1) {
let quantity = cartItems[i].quantity;
let price = items.filter((el) => el.id === cartItems[i].itemId)[0]
.price;
total.price = total.price + quantity * price;
total.quantity = total.quantity + quantity;
}
}
return total;
};
const renderItems = items.filter(
(el) => cartItems.map((el) => el.itemId).indexOf(el.id) > -1
);
const total = getTotal();
return (
<div id="item-list-container">
<div id="item-list-body">
<div id="item-list-title">장바구니</div>
<span id="shopping-cart-select-all">
<input
type="checkbox"
checked={checkedItems.length === cartItems.length ? true : false}
onChange={(e) => handleAllCheck(e.target.checked)}
></input>
<label>전체선택</label>
</span>
<div id="shopping-cart-container">
{!cartItems.length ? (
<div id="item-list-text">장바구니에 아이템이 없습니다.</div>
) : (
<div id="cart-item-list">
{renderItems.map((item, idx) => {
const quantity = cartItems.filter(
(el) => el.itemId === item.id
)[0].quantity;
return (
<CartItem
key={idx}
handleCheckChange={handleCheckChange}
handleQuantityChange={handleQuantityChange}
handleDelete={handleDelete}
item={item}
checkedItems={checkedItems}
quantity={quantity}
/>
);
})}
</div>
)}
<OrderSummary total={total.price} totalQty={total.quantity} />
</div>
</div>
</div>
);
}
import React from "react";
import { addToCart, notify } from "../actions/index";
import { useSelector, useDispatch } from "react-redux";
import Item from "../components/Item";
function ItemListContainer() {
const state = useSelector((state) => state.itemReducer);
const { items, cartItems } = state;
const dispatch = useDispatch();
const handleClick = (item) => {
if (!cartItems.map((el) => el.itemId).includes(item.id)) {
//TODO: dispatch 함수를 호출하여 아이템 추가에 대한 액션을 전달하세요.
dispatch(addToCart(item.id));
dispatch(notify(`장바구니에 ${item.name}이(가) 추가되었습니다.`));
} else {
dispatch(notify("이미 추가된 상품입니다."));
}
};
return (
<div id="item-list-container">
<div id="item-list-body">
<div id="item-list-title">쓸모없는 선물 모음</div>
{items.map((item, idx) => (
<Item
item={item}
key={idx}
handleClick={() => {
handleClick(item);
}}
/>
))}
</div>
</div>
);
}
export default ItemListContainer;
import { REMOVE_FROM_CART, ADD_TO_CART, SET_QUANTITY } from "../actions/index";
import { initialState } from "./initialState";
const itemReducer = (state = initialState, action) => {
switch (action.type) {
case ADD_TO_CART:
//TODO 추가
return Object.assign({}, state, {
cartItems: [...state.cartItems, action.payload],
});
break;
case REMOVE_FROM_CART:
//TODO
let del = state.cartItems.filter(
(el) => el.itemId !== action.payload.itemId
);
return Object.assign({}, state, {
cartItems: del,
});
break;
case SET_QUANTITY:
let idx = state.cartItems.findIndex(
(el) => el.itemId === action.payload.itemId
);
//TODO
return Object.assign({}, state, {
cartItems: [
...state.cartItems.slice(0, idx),
action.payload,
...state.cartItems.slice(idx + 1),
],
});
break;
default:
return state;
}
};
export default itemReducer;
첫날 페어님과 함께 이론공부를 한 후 바로 문제를 보니 어느정도 문제가 해결이 됐지만 아직 이해가 안 가는 코드가 몇몇 있어서 다시 복습을 해아할 것 같다!
그리고 줌수업을 들으면서 느낀건데 문제를 클론하고 폴더와 파일들 구조를 먼저 파악하는 습관을 가져야겠단 생각이 들었다. 문서들의 구조를 알고(컴포넌트 폴더, 리듀서파일 등등) 문제에 접근하니 문제를 파악하기 더 수월해지는 느낌이었다! 앞으로 과제에서는 시작 전에 꼭 문서구조를 파악하고 시작해야겠다!