한번 써보고 싶어서 저장해둔 밈
상태 관리 라이브러리 중 하나인 redux를 배우기에 앞서 sprint cmarket hooks 과제를 진행했다. 이번 과제의 목표는 redux 준비운동으로 state hook으로 구현한다.
Cmarket 앱의 컴포넌트 구조와 데이터 흐름을 간단하게 그려보니,
주요 상태를 이해하는 데 도움이 되었다.
app
의 포함된 모든 컴포넌트에서 items
와 cartItems
를 사용하고 있기 때문에 상단의 app
컴포넌트에서 상태를 관리한다.
(Handler가 ItemListContainer
에 이미 작성되어있었기 때문에)
Item
컴포넌트에 위치한 실제 button
을 클릭했을 때 상태를 변경하기 위해서 addCartItem Handler
를 prop
으로 전달하고 있다.
버튼을 클릭했을 때 해당 아이템의 id
값을 상위 컴포넌트인 ItemListContainer
로 전달한다.
addCartItem Handler
에서 cartItems
값을 변경하기 위해서 setCartItems
를 prop
으로 내려준다. 이제 addCartItem Handler
작성해야 할 내용은..
id
를 가진 객체를 배열에 추가한다. cartItems
와 연결된 Nav
컴포넌트의 수량이 업데이트되지 않는 문제가 있었다. 그 이유는 state
데이터를 직접 변경했기 때문이었다. (정확히는 cartItems
배열을 직접 수정했기 때문에.) setCartItems
로도 변경해 주었지만, react는 주소 값으로 랜더 여부를 결정하기 때문에 리랜더링이 되지 않는 것이었다.
map
과filter
는 새로운 배열을 반환하기 때문에 이럴때 유용하다.
// ItemListContainer.js
const handleClick = (id) => {
if (cartItems.some(el => el.itemId === id)) {
cartItems.map(elem => elem.itemId === id ? elem.quantity += 1 : elem)
} else {
setCartItems([...cartItems, {itemId: id, quantity: 1}])
}
}
마찬가지로 장바구니 아이템을 삭제하기 위해서 deleteCartItem Handler
를 실제 버튼이 있는 CartItem
컴포넌트에 prop
으로 전달한다.
deleteHandler
에서는 cartItems
에서 동일한 id
를 가진 아이템을 삭제한다. filter
로 해당 아이템을 제외한 새로운 배열을 만드는 것으로 구현했다.
// ShoppingCart.js
const handleDelete = (itemId) => {
setCheckedItems(checkedItems.filter((el) => el !== itemId))
const deleted = cartItems.filter((elem) => elem.itemId !== itemId)
setCartItems(deleted)
}
페어 프로그래밍 시간에 놓친 부분인 것 같아서 따로 구현해보았는데, 생각보다 배열 데이터를 가공하는데 애를 먹었다.
const handleQuantityChange = (quantity, itemId) => {
const filtered = cartItems.filter((elem) => elem.itemId === itemId)[0]
const filteredIdx = cartItems.indexOf(filtered)
setCartItems([
...cartItems.slice(0, filteredIdx),
{ itemId, quantity },
...cartItems.slice(filteredIdx + 1)
])
}
처음엔 splice를 써야하나 싶었지만, 자르는 갯수가 정해지지 않아 slice가 더 적절해 보였다. 더 좋은 방법이 있다면 소개를..😄 (환영합니다. __무너진 제4의 벽)
Redux가기전에 간단한? Props Drilling에 알아보는 시간이었지만 현실은 데이터 가공. setState를 prop으로 내릴 수 있다는 것은 확실히 알았다. 그리고 직접 데이터 흐름을 그려 보면서 상태 관리 라이브러리가 얼마나 효과적일지 기대가 되었다. 🤔