코드스테이츠 9주차 / Cmarket (Hooks)
✏️Achievement Goals
✅ useState 를 이용해 상태를 사용하는 방법을 학습합니다.
✅ [장바구니 담기] 버튼을 이용해 장바구니에 해당 상품이 추가되도록 구현하세요.
✅ 장바구니 내 [삭제] 버튼을 이용해 장바구니의 상품이 제거되도록 구현하세요.
✅ 장바구니의 상품 갯수의 변동이 생길 때마다, 상단 내비게이션 바에 상품 갯수가 업데이트되도록 구현하세요.
📝summary
이미 어느정도 완성되어있는 쇼핑몰 홈페이지가 있었고 여기서
장바구니 담기, 삭제, 상품갯수 업데이트기능을 작동하도록 구현하면 됐다
제일 상위폴더인 app.js에 이미 작성되어있는 아이템들의 정보와 카트에 담겨있는 아이템의 정보를 각 컴포넌트에 내려줘서 문제를 해결 할 수 있다
const [items, setItems] = useState(initialState.items);
const [cartItems, setCartItems] = useState(initialState.cartItems);
이미 각 컴포넌트에 handleClick들이 이름만 작성이 되어 있어서
그곳을 채워넣는 형태로 구현해 줬다
여기서 중요한것은
setItems
setCartItems
변경하는 함수들도 props로 넘겨줄 수 있다는 것!
app.js
import React, { useState } from 'react';
import Nav from './components/Nav';
import ItemListContainer from './pages/ItemListContainer';
import './App.css';
import {
BrowserRouter as Router,
Switch,
Route,
} from "react-router-dom";
import ShoppingCart from './pages/ShoppingCart';
import { initialState } from './assets/state';
function App() {
const [items, setItems] = useState(initialState.items);
const [cartItems, setCartItems] = useState(initialState.cartItems);
return (
<Router>
<Nav cartItems={cartItems}/>
<Switch>
<Route exact={true} path="/">
// cartItems={cartItems} setCartItems={setCartItems}을 props로 넘겨준다
<ItemListContainer items={items} cartItems={cartItems} setCartItems={setCartItems }/>
</Route>
<Route path="/shoppingcart">
<ShoppingCart cartItems={cartItems} items={items} setCartItems={setCartItems}/>
</Route>
</Switch>
</Router>
);
}
export default App;
1. 장바구니 추가
ItemListContainer의 handleClick을 item 컴포넌트에서 받아서 사용해 장바구니에 추가해주는 형태였다
이때 item의 장바구니 담기 onClick에 onClick={(e) => handleClick(e, item.id)}
이미 인자가 두개가 작성이 되어 있는걸 보고 시작해야 한다
장바구니에 담기를 구현할때는 고려해줘야 할 사항이 몇가지 있었는데
장바구니에 없는 아이템은 추가해주고
있는 아이템이면 수량만 추가해줘야 했다
ItemListContainer.js
import React from 'react';
import Item from '../components/Item';
function ItemListContainer({ items, cartItems, setCartItems }) {
const handleClick = ( e, id ) => {
let newCartItem = {};
newCartItem.itemId = id;
newCartItem.quantity = 1;
for(let i = 0; i < cartItems.length; i++){
if(cartItems[i].itemId === id){
setCartItems([...cartItems])
cartItems[i].quantity++
}else{
setCartItems([...cartItems, newCartItem])
}
}
}
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} />)}
</div>
</div>
);
}
export default ItemListContainer;
나는 포문을 돌려서 해결했는데 페어님은 findIndex를 사용해서 구현한 코드가 인상적이었다
const handleClick = (e, Id) => {
let original= cartItems
let findNumber = cartItems.findIndex((e)=>e.itemId===Id)
if(findNumber!==-1){
original[findNumber].quantity+=1
setCartItems(original)
}else {
setCartItems ([...original,
{"itemId": Id,"quantity": 1}])
}
클릭을 하면 클릭한 값의 id를 가져온다.
클릭한 값과 현재 있는 값의 id 를 비교해서 있으면 그냥 추가만 하고
없으면 새로운 값을 추가해준다.
item.js
import React from 'react'
export default function Item({ item, handleClick }) {
return (
<div key={item.id} className="item">
<img className="item-img" src={item.img} alt={item.name}></img>
<span className="item-name">{item.name}</span>
<span className="item-price">{item.price}</span>
<button className="item-button" onClick={(e) => handleClick(e, item.id)}>장바구니 담기</button>
</div>
)
}
2. 장바구니 삭제
handleDelete함수를 수정해서 사용하면 된다
setCartItems(cartItems.filter((el)=> el.itemId !== itemId))
cartItems에 필터를 돌려서 cartItems의 el은 객체형태로 되어있으므로 el.itemId와 클릭된 itemId값을 비교해서 같은 값만 삭제되도록 하면 된다
3. 장바구니 수량 변경
handleQuantityChange함수를 수정해서 사용하면 된다
const handleQuantityChange = (quantity, itemId) => {
const newCartItems = [...cartItems]
let findIdx = cartItems.findIndex((item) => item.itemId === itemId)
newCartItems[findIdx].quantity = quantity
setCartItems(newCartItems)
}
findIndex를 사용해서 인덱스 번호를 찾아준 뒤
newCartItems[인덱스번호]의 수량 = 받아온 수량 으로 바꿔주면 된다
이렇게 하면 장바구니에서 위 아래 버튼을 클릭해서도 수량 변경이 가능하다
"cartItems": [
{
"itemId": 1,
"quantity": 1
},
{
"itemId": 5,
"quantity": 7
},
{
"itemId": 2,
"quantity": 3
}
]
ShoppingCart.js
import React, { useState } from 'react'
import CartItem from '../components/CartItem'
import OrderSummary from '../components/OrderSummary'
export default function ShoppingCart({ items, cartItems, setCartItems }) {
const [checkedItems, setCheckedItems] = useState(cartItems.map((el) => el.itemId))
const handleQuantityChange = (quantity, itemId) => {
const newCartItems = [...cartItems]
let findIdx = cartItems.findIndex((item) => item.itemId === itemId)
newCartItems[findIdx].quantity = quantity
setCartItems(newCartItems)
}
const handleDelete = (itemId) => {
setCheckedItems(checkedItems.filter((el) => el !== itemId))
setCartItems(cartItems.filter((el)=> el.itemId !== itemId))
}
return (
<div id="item-list-container">
<div id="item-list-body">
<div id="item-list-title">장바구니</div>
<span id="shopping-cart-select-all">
<input
type="checkbox"
checked={
checkedItems.length === cartItems.length ? true : false
}
onChange={(e) => handleAllCheck(e.target.checked)} >
</input>
<label >전체선택</label>
</span>
<div id="shopping-cart-container">
{!cartItems.length ? (
<div id="item-list-text">
장바구니에 아이템이 없습니다.
</div>
) : (
<div id="cart-item-list">
{renderItems.map((item, idx) => {
const quantity = cartItems.filter(el => el.itemId === item.id)[0].quantity
return <CartItem
key={idx}
handleCheckChange={handleCheckChange}
handleQuantityChange={handleQuantityChange}
handleDelete={handleDelete}
item={item}
checkedItems={checkedItems}
quantity={quantity}
/>
})}
</div>
)}
<OrderSummary total={total.price} totalQty={total.quantity} />
</div>
</div >
</div>
)
}
CartItem.js
import React from 'react'
export default function CartItem({
item,
checkedItems,
handleCheckChange,
handleQuantityChange,
handleDelete,
quantity
}) {
return (
<li className="cart-item-body">
<input
type="checkbox"
className="cart-item-checkbox"
onChange={(e) => {
handleCheckChange(e.target.checked, item.id)
}}
checked={checkedItems.includes(item.id) ? true : false} >
</input>
<div className="cart-item-thumbnail">
<img src={item.img} alt={item.name} />
</div>
<div className="cart-item-info">
<div className="cart-item-title" data-testid={item.name}>{item.name}</div>
<div className="cart-item-price">{item.price} 원</div>
</div>
<input
type="number"
min={1}
className="cart-item-quantity"
value={quantity}
onChange={(e) => {
handleQuantityChange(Number(e.target.value), item.id)
}}>
</input>
<button className="cart-item-delete" onClick={() => { handleDelete(item.id) }}>삭제</button>
</li >
)
}
4. Nav바 장바구니 수량 변경
제일 쉬운 부분이다
app.js에서 nav바에 cartItems을 넘겨준뒤
app.js
<Nav cartItems={cartItems}/>
nav.js
{cartItems.length}
로 수량을 바꿔주면 끝난다
import React from 'react';
import { Link } from 'react-router-dom';
function Nav({cartItems}) {
return (
<div id="nav-body">
<span id="title">
<img id="logo" src="../logo.png" alt="logo" />
<span id="name">CMarket</span>
</span>
<div id="menu">
<Link to="/">상품리스트</Link>
<Link to="/shoppingcart">
장바구니<span id="nav-item-counter">{cartItems.length}</span>
</Link>
</div>
</div>
);
}
export default Nav;
시작하기 전에...
애플리케이션에서 사용하는 주요 상태는 다음과 같습니다. 시작하기 전에 반드시 컴포넌트 구조와, 데이터 흐름을 먼저 그림으로 그려보세요. 일을 보다 단순하게 만들 수 있습니다.
다시 한번 컴포넌트 구조를 파악하고 시작하는게 중요하구나 깨달았던 하루
너무 어렵다^~^
Author And Source
이 문제에 관하여(코드스테이츠 9주차 / Cmarket (Hooks)), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@support/코드스테이츠-9주차-Cmarket-Hooks저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)