React Hooks를 사용한 상태 관리하기 (+ sprint cMarket)

김민아·2022년 8월 31일
0
post-thumbnail

한번 써보고 싶어서 저장해둔 밈

React Hooks를 사용한 상태 관리

개요

상태 관리 라이브러리 중 하나인 redux를 배우기에 앞서 sprint cmarket hooks 과제를 진행했다. 이번 과제의 목표는 redux 준비운동으로 state hook으로 구현한다.

  • 장바구니 아이템을 추가하고,
  • 장바구니에서 아이템 수량을 변경하고,
  • 장바구니에서 아이템을 삭제해 보는 것

상태 관리

Cmarket 앱의 컴포넌트 구조와 데이터 흐름을 간단하게 그려보니,
주요 상태를 이해하는 데 도움이 되었다.

1. app에서 관리되는 상태 items와 cartItems

app의 포함된 모든 컴포넌트에서 itemscartItems를 사용하고 있기 때문에 상단의 app 컴포넌트에서 상태를 관리한다.


2. 상태 끌어올리기

(Handler가 ItemListContainer에 이미 작성되어있었기 때문에)
Item 컴포넌트에 위치한 실제 button을 클릭했을 때 상태를 변경하기 위해서 addCartItem Handlerprop으로 전달하고 있다.
버튼을 클릭했을 때 해당 아이템의 id 값을 상위 컴포넌트인 ItemListContainer로 전달한다.


3. 장바구니에 아이템 추가하기

addCartItem Handler에서 cartItems 값을 변경하기 위해서 setCartItemsprop으로 내려준다. 이제 addCartItem Handler 작성해야 할 내용은..

  • 기존에 장바구니에 추가가 된 아이템인지 확인한다.
    - 기존에 있는 아이템이라면, 수량을 하나 추가한다.
    - 기존에 없는 아이템이라면, 기본 수량 1개와 전달인자로 가져온 id를 가진 객체를 배열에 추가한다.

🛑 장바구니 수량이 업데이트 되지 않는 문제

cartItems와 연결된 Nav 컴포넌트의 수량이 업데이트되지 않는 문제가 있었다. 그 이유는 state 데이터를 직접 변경했기 때문이었다. (정확히는 cartItems 배열을 직접 수정했기 때문에.) setCartItems로도 변경해 주었지만, react는 주소 값으로 랜더 여부를 결정하기 때문에 리랜더링이 되지 않는 것이었다.

mapfilter는 새로운 배열을 반환하기 때문에 이럴때 유용하다.

// 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}])      
    }
}

4. 장바구니에서 아이템 삭제하기

마찬가지로 장바구니 아이템을 삭제하기 위해서 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)
}

5. 장바구니의 수량 변경하기

페어 프로그래밍 시간에 놓친 부분인 것 같아서 따로 구현해보았는데, 생각보다 배열 데이터를 가공하는데 애를 먹었다.

  • 먼저 선택한 id와 동일한 객체를 찾는다 (filter)
  • 해당 객체가 배열의 몇번째 idx에 있는지 값을 가져온다. (indexOf)
  • idx를 기준으로 배열을 자리고 객체를 수정한 수 다시 합친다.

  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으로 내릴 수 있다는 것은 확실히 알았다. 그리고 직접 데이터 흐름을 그려 보면서 상태 관리 라이브러리가 얼마나 효과적일지 기대가 되었다. 🤔

0개의 댓글