티스토리 뷰

코딩/코드스테이츠

[Redux] - Cmarket Redux

김기지 2023. 2. 27. 09:55

✅ Todo…

Shopping Cart Actions

✓ addToCart는 ADD_TO_CART 액션을 생성해야 합니다

✓ removeFromCart는 REMOVE_FROM_CART 액션을 생성해야 합니다

✓ setQuantity는 SET_QUANTITY 액션을 생성해야 합니다

Item Reducer

✓ ADD_TO_CART 액션에 따라 cartItems 상태가 변해야 합니다

✓ REMOVE_FROM_CART 액션에 따라 cartItems 상태가 변해야 합니다

✓ SET_QUANTITY 액션에 따라 cartItems 상태가 변해야 합니다

✓ 리듀서는 다른 상태의 값을 보존해야 합니다

Shopping Pages

✓ ShoppingCart에 cartItems가 렌더되어야합니다.

✓ ADD_TO_CART 액션에 따라 ShoppingCart가 렌더되어야 합니다.

✓ REMOVE_FROM_CART 액션에 따라 ShoppingCart가 렌더되어야 합니다.

✓ SET_QUANTITY 액션에 따라 OrderSummary가 렌더되어야 합니다.

✓ Checkbox의 상태에 따라 OrderSummary가 렌더되어야 합니다.


1. Action

어떤 액션을 취할 것인지 정의

type은 필수로 지정을 해 주어야 하며, 그 외의 것들은 선택적으로 사용할 수 있다.

actionCreator(액션 생성자)가 해주는 역할은 단순히 객체를 반환하는 것이다.

payload → 추가로 넘기고 싶은 값을 key와 함께 보내는 것

// 아이템을 추가하는 액션 생성자
export const addToCart = (itemId) => {
  return {
	// type은 ADD_TO_CART
    type: ADD_TO_CART,
    // 추가로 보내줄 key와 value
    payload: {
    	// 처음 들어오는 아이템의 quantity는 1
      quantity: 1,
        // 아이템 고유의 id값으로 구분하기 위함
      itemId
    }
  }
}
// 아이템을 삭제하는 액션 생성자
export const removeFromCart = (itemId) => {
  return {
    // type은 REMOVE_FROM_CART
    type: REMOVE_FROM_CART,
    // 추가로 보내줄 key와 value
    payload: {
    	// 삭제할 때는 아이템의 id값으로 구분하기 위함 -> quantity는 딱히 필요x
      itemId
    }
  }
}
// 아이템이 담기는 갯수를 수정하는 액션 생성자
export const setQuantity = (itemId, quantity) => {
  return {
    // type은 SET_QUANTITY
    type: SET_QUANTITY,
    // 추가로 보내줄 key와 value
    payload: {
    // 넘겨받는 quantity를 수정함
      quantity,
    // item의 id로 구분하기 위함
      itemId
    }
  }
}

2. Dispatch

Action을 전달하는 메서드

🧚 ItemListContainer.js

// actionCreactor addToCart를 불러옴
import { addToCart, notify } from '../actions/index';
...
function ItemListContainer() {
	...
	// useDispatch()-> Action 객체를 Reducer로 전달해 주는 Dispatch 함수를 반환하는 메서드
  const dispatch = useDispatch();
	// 아이템 클릭시 실행될 함수
  const handleClick = (item) => {
    if (!cartItems.map((el) => el.itemId).includes(item.id)) {
  	// dispatch()메소드로 Action 객체를 전달
    // 여기서는 액션 생성자(Action Creator)를 사용
      dispatch(addToCart(item.id))
      dispatch(notify(`장바구니에 ${item.name}이(가) 추가되었습니다.`))
    }
    else {
      dispatch(notify('이미 추가된 상품입니다.'))
    }
  }
...
}

 

🧚 ShoppingCart.js

// actionCreactor removeFromCart, setQuantity를 불러옴
import { removeFromCart, setQuantity } from '../actions';
...
export default function ShoppingCart() {
	...
	// useDispatch()-> Action 객체를 Reducer로 전달해 주는 Dispatch 함수를 반환하는 메서드
  const dispatch = useDispatch();
	...
  const handleQuantityChange = (quantity, itemId) => {
    // dispatch()메소드로 setQuantity 액션 생성자를 전달
    // 여기서 itemId와 quantity의 순서를 바꾸어 전달하면 값이 뒤바뀜 -> 콘솔창에 찍었을 때 바뀌어서 찍힘
    dispatch(setQuantity(itemId,quantity));
  }
  const handleDelete = (itemId) => {
    setCheckedItems(checkedItems.filter((el) => el !== itemId))
    // dispatch()메소드로 removeFromCart 액션 생성자를 전달
    dispatch(removeFromCart(itemId))
  }
  ...
  const renderItems = items.filter((el) => cartItems.map((el) => el.itemId).indexOf(el.id) > -1)
  const total = getTotal()
  return (
    ...
  )
}

3. Store

state가 관리되는 오직 하나뿐인 저장소

createStore메서드를 활용해 Reducer를 연결한다.

import { compose, createStore, applyMiddleware } from "redux";
...
const store = createStore(rootReducer, composeEnhancers(applyMiddleware(thunk)));

4. Reducer

현재의 state와 Action을 이용해서 새로운 state를 만들어 내는 순수함수
  • 참고한 코드 - 쇼핑몰에서 크게 볼 수 있는 장바구니 추가 액션에 대한 코드
const itemReducer = (state = initialState, action) => {
  switch (action.type) {
    // action.type이 ADD_TO_CART일 때
    case ADD_TO_CART:
    // Object.assign()으로 객체를 병합
    // spread연산자로 기존의 cartItems를 가져오고,
    // action객체에 추가로 보내준 payload안의 key-value를 추가
      return Object.assign({}, state, {
        cartItems: [...state.cartItems, action.payload]
    // state를 직접 바꾸면 안됌 -> 항상 새로운 객체를 만들어 리턴해야함
    // Reducer의 Immutability(불변성)을 위해 Object.assign메소드로 새로운 객체를 만듬
      })
    default:
      return state;
  }
}

나머지 REMOVE_FROM_CART SET_QUANTITY 액션에 대한 코드 작성한다.

const itemReducer = (state = initialState, action) => {

  switch (action.type) {
    case ADD_TO_CART:
      return Object.assign({}, state, {
        cartItems: [...state.cartItems, action.payload]
      })
    case REMOVE_FROM_CART:
    // 전달된 action의 payload로 넘어온 itemId와 같은 요소를 제외한 배열만 가져오기
     const newCartItems=state.cartItems.filter((el)=>el.itemId !== action.payload.itemId);
    // newCartItems는 필터링된 cartItems의 요소만 담긴 배열이므로 형태를 맞춰줌
			return Object.assign({},state,{cartItems:newCartItems});
    case SET_QUANTITY:
    // action의 payload로 넘어온 itemId와 같은 요소의 index값
      let idx = state.cartItems.findIndex(el => el.itemId === action.payload.itemId)
      // idx를 어떻게 활용?
        // 처음 시도는 해당 idx의 요소만 가져와 quantity를 바꿔주었음 -> 실패!
        // idx를 기준으로 앞/뒤로 배열을 나누어 나중에 합치기
        let frontItems = state.cartItems.slice(0,idx);
        let lastItems = state.cartItems.slice(idx+1);
      return Object.assign({},state,{cartItems:[...frontItems,action.payload,...lastItems]});
    default:
      return state;
  }
}
728x90

'코딩 > 코드스테이츠' 카테고리의 다른 글

[사용자 친화 웹] - 접근성  (0) 2023.03.02
[사용자 친화 웹] - 웹 표준  (0) 2023.02.28
[React] - 상태 관리 (Redux)  (0) 2023.02.24
[React] 상태 관리  (0) 2023.02.23
[React] -useRef  (0) 2023.02.22