티스토리 뷰
✅ 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;
}
}
'코딩 > 코드스테이츠' 카테고리의 다른 글
[사용자 친화 웹] - 접근성 (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 |