Action
객체 생성Action
객체가 Dispatch
에게 전달Dispatch
함수가 Action
객체를 Reducer
함수로 전달Reducer
함수는 Action
객체를 확인하고 전역 상태 저장소인 Store
의 상태를 변경 ... 생략
// 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: {
itemId,
quantity
}
}
}
...생략
import React from 'react';
import { addToCart, notify } from '../actions/index';
import { useSelector, useDispatch } from 'react-redux';
import Item from '../components/Item';
function ItemListContainer() {
//itemReducer의 상태만 선택해서 가져다줘
const state = useSelector(state => state.itemReducer);
const { items, cartItems } = state;
const dispatch = useDispatch();
const handleClick = (item) => {
// 아이템 페이지에서 클릭된 아이템이 id가 장바구니에 포함되어 있지 않다면 아이템을 추가해줘!
if (!cartItems.map((el) => el.itemId).includes(item.id)) {
//TODO: dispatch 함수를 호출하여 아이템 추가에 대한 액션을 전달하세요.
dispatch(addToCart(item.id))
dispatch(notify(`장바구니에 ${item.name}이(가) 추가되었습니다.`))
}
else {
dispatch(addToCart(item.id))
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;
... 생략
const handleQuantityChange = (quantity, itemId) => {
//TODO: dispatch 함수를 호출하여 액션을 전달하세요.
dispatch(setQuantity(itemId,quantity))
}
const handleDelete = (itemId) => {
setCheckedItems(checkedItems.filter((el) => el !== itemId))
//TODO: dispatch 함수를 호출하여 액션을 전달하세요.
dispatch(removeFromCart(itemId))
}
...생략
📍 새로 알게된 내용
// 새로운 state를 만들어서 기존의 state와 장바구니에 액션을 통해
// 추가되어지는 아이템을 객체에 담아 리턴함
// 3번째 인자에는 변경해주고 싶은 값을 전달
else {
return Object.assign( {}, state, {
cartItems: [...state.cartItems, action.payload]
})
else {
return {
// 기존 배열을 복사
...state,
// 카트아이템 복사와 액션에 따른 상태를 구현
cartItems: [...state.cartItems, action.payload]
}
}
[전체코드]
import { REMOVE_FROM_CART, ADD_TO_CART, SET_QUANTITY } from "../actions/index";
import { initialState } from "./initialState";
//state는 초기상태의 배열을 담고 있다
const itemReducer = (state = initialState, action) => {
switch (action.type) {
case ADD_TO_CART:
// 장바구니에 있는 itemId과 액션이 발생했을때의 itemID이 동일하다면 해당 index를 찾음
let existItem = state.cartItems.findIndex(el => el.itemId === action.payload.itemId)
// 해당 인덱스가 없다면 양만 늘려줘
if(existItem !== -1) {
// 상태값을 직접 건드리면 안되서 복사를 하기
let newcartItems = [...state.cartItems]
// 장바구니의 특정 인덱스의 수량을 늘려주기
newcartItems[existItem].quantity += 1
return {
...state,
cartItems : newcartItems
}
}
else {
return {
...state,
cartItems: [...state.cartItems, action.payload]
}
}
break;
case REMOVE_FROM_CART:
// 삭제하려고 하는 아이템 외의 아이템들만 걸러줘
let deleteItem = state.cartItems.filter((el) => el.itemId !== action.payload.itemId)
return Object.assign({}, state, {
cartItems: deleteItem
})
break;
case SET_QUANTITY:
// 상태에 있는 카트아이템에서 액션이 발생되어지는(상태가 변화) 동일한 itemId의 값을 idx에 담아줘
let idx = state.cartItems.findIndex(el => el.itemId === action.payload.itemId)
// 카트아이템 배열 복사
let newQuantity = [...state.cartItems]
// 배열의 idx의 quantity 수량을 액션의 상태에 맞게 바꿔줘
newQuantity[idx].quantity = action.payload.quantity
console.log(newQuantity)
return {
...state,
cartItems: newQuantity
// [레퍼런스]
// 1. 카트 아이템에서 0부터 인덱스 전까지의 요소들로 이루어진 배열 생성
// 2. 액션 객체에서 전달된 데이터로 카트 아이템의 수량이 포함됨
// 3. 카트 아이템에서 배열에서 인덱스부터 마지막 인덱스까지의 요소들로 이루어진 배열생성
// [...state.cartItems.slice(0,idx), action.payload, ...state.cartItems.slice(idx+1)]
}
break;
default:
// 아무 조건도 만족하지 않는 경우
return state;
}
}
export default itemReducer;
React Hooks 과제 파일 구조랑 코드 분석을 꼼꼼히 했더니 이번 과제는 그때만큼 어렵지는 않았다. test케이스가 있어서 그런지 뭘 먼저 건드려야할지 알 것 같은 느낌이랄까..?ㅎㅎ 그래도 해결아되는 부분 코드를 2시간씩 고민하면서 해결한 희열을 잊지 못한 과제였다... 후.. 힘든만큼 보람이 있었던 과제였던 것 같다