-
아마존 E-commerce 클론 -8) add to Cart 버튼 기능 만들고 redux store에 카트정보 넣어서 활용하기NODE.JS 2021. 5. 27. 13:23
먼저 재고가 0개 이상이라면 상품의 선택란을 표기하는 부분이 생기도록 해보자.
productscreen.js를 다음과 같이 설정해준다.
import React,{useEffect, useState} from 'react' const [qty, setQty] = useState(1); </li> {product.countInStock > 0 && ( <> <li> <div className="row"> <div>Qty</div> <div> <select value={qty} onChange={(e)=> setQty(e.target.value)}> {[...Array(product.countInStock).keys()].map( (x)=>(<option key={x+1}value={x+1}>{x+1}</option> ) )} </select> </div> </div> </li> <li> <button onClick={addToCartHandler} className="primary block"> Add to Cart </button> </li> </> )} </ul>
addToCarthandler함수도 설정해주자.
const addToCartHandler = () =>{ props.history.push(`/cart/${productId}?qty=${qty}`); }
클릭하면 페이지를 이동하도록 설정했으니,
cartscreen을 만들어준다.
import React from 'react' function CartScree(props) { const productId = props.match.params.id const qty = props.location.search? Number(props.location.search.split('=')[1]):1 return ( <div> <h1>Cart Screen</h1> <p>ADD TO CART : ProductId:{productId} Qty : {qty}</p> </div> ) } export default CartScree
스크린을만들었으니 App.js에 추가해준다.
import CartScreen from './screens/CartScreen'; function App() { return ( <BrowserRouter > <Route path="/cart/:id?" component={CartScreen} exact></Route> </BrowserRouter> ); } export default App;
상품 선택가능 카트페이지로 가면 몇개가 추가되었는지 아이디와 함께 학인할 수 있다. 마지막으로 index.css에서 스타일링을 해보자.
원래 버튼만 설정했던 것을 범위를 넓혀서 모두 적용되도록 할 것이다.
input, select, textarea,button{ padding:1rem; border-radius: 0.5rem; border:0.1rem #a4a4a4 solid; font-family: Helvetica, Arial, sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } button{ background-color: #f8f8f8; cursor:pointer } input:hover, select:hover, textarea:hover, button:hover{ border: 0.1rem #404040 solid; } button.primary{ background-color: #f0c040; } button.block{ width:100%; }
이제 리덕스 스토어에 카트 정보도 넣어서 이용하도록 하자.
우선 상수를 추가해주자.
constants>cartConstants를 만들어준다.
export const CART_ADD_ITEM = 'CART_ADD_ITEM'
그다음 액션을 생성해준다.
actions>cartActions.js
import Axios from "axios"; import { CART_ADD_ITEM } from "../constants/cartConstants"; export const addToCart = (productId, qty)=> async(dispatch, getState)=>{ const {data} =await Axios.get(`/api/products/${productId}`); dispatch({ type:CART_ADD_ITEM, payload:{ name:data.name, image:data.image, price:data.price, countInStock:data.countInStock, product:data._id, qty, } }) }
리듀서를 생성해준다.
reducers>cartReducers.js
이미 아이템이 있으면 그 아이템을 이용하고 없으면 새로운 아이템을 추가해서 돌려준다.
import { CART_ADD_ITEM } from "../constants/cartConstants"; export const cartReducer = (state={cartItems:[]}, action)=>{ switch(action.type){ case CART_ADD_ITEM: const item = action.payload; const existItem = state.cartItems.find(x=>x.product===item.product) if(existItem){ return { ...state, cartItems:state.cartItems.map(x=>x.product===existItem.product? item:x) } }else{ return {...state, cartItems:[...state.cartItems, item]}; } default: return state; } }
store.js에 다음과 같이 추가해준다.
import{combineReducers, createStore, compose, applyMiddleware} from 'redux'; import thunk from 'redux-thunk' import { cartReducer } from './reducers/cartReducers'; import { productDetailsReducer, productListReducer } from './reducers/productReducers'; const initialState = {}; const reducer = combineReducers({ productList: productListReducer, productDetails : productDetailsReducer, cart:cartReducer, }) const composeEnhancer = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__||compose; const store = createStore(reducer, initialState, composeEnhancer(applyMiddleware(thunk))); export default store;
카트 스크린에서 다음과 같이 디스패치를 추가해준다.
import React, { useEffect } from 'react' import {useDispatch} from 'react-redux' import { addToCart } from '../actions/cartActions' function CartScree(props) { const productId = props.match.params.id const qty = props.location.search? Number(props.location.search.split('=')[1]):1 const dispatch = useDispatch() useEffect(()=>{ if(productId){ dispatch(addToCart(productId, qty)) } },[dispatch, productId, qty]) return ( <div> <h1>Cart Screen</h1> <p>ADD TO CART : ProductId:{productId} Qty : {qty}</p> </div> ) } export default CartScree
마지막으로 component의 Product.js, App.js에서
react-router-dom의 Link를 이용해서
a 를 모두 LInk, Link to 로 변경해주자.
import React from 'react' import Rating from './Rating'; import {Link} from 'react-router-dom' function Product(props) { const{product} = props; return ( <div key={product._id} className="card"> <Link to={`/product/${product._id}`}> <img className="medium" src={product.image} alt={product.name}/> </Link> <div className="card-body"> <Link to={`/product/${product._id}`}> <h2>{product.name}</h2> </Link> <Rating rating ={product.rating} numReviews={product.numReviews}/> <div className="price"> ${product.price} </div> </div> </div> ) } export default Product
import React from 'react'; import {BrowserRouter,Route} from 'react-router-dom' import ProductScreen from './screens/ProductScreen'; import HomeScreen from './screens/HomeScreen'; import CartScreen from './screens/CartScreen'; import {Link} from 'react-router-dom' function App() { return ( <BrowserRouter > <div className="grid-container"> <header className="row"> <div> <Link className="brand" href="/">amazona</Link> </div> <div> <Link to="/cart">Cart</Link> <Link to="/signin">Sign In</Link> </div> </header> <main> <Route path="/" component={HomeScreen} exact></Route> <Route path="/product/:id" component={ProductScreen} exact></Route> <Route path="/cart/:id?" component={CartScreen} exact></Route> </main> <footer className="row center">All right reserved</footer> </div> </BrowserRouter> ); } export default App;
add to cart 버튼을 누르면 다음과 같이 리덕스에 추가된 것을 확인할 수 있다.
다른 아이템을 추가하면 카트 아이템 리스트가 변경된 것을 확인할 수 있다. 우리가 만약 카트저장을 해놓고 랜딩페이지에가서 다시화면을 리프레시 하면 카트아이템이 빈 배열로 되는데,
이는 우리가 로컬 저장소에 저장하지않았기 때문이다.
action에 가서 다음과 같이 수정해준다.
import Axios from "axios"; import { CART_ADD_ITEM } from "../constants/cartConstants"; export const addToCart = (productId, qty)=> async(dispatch, getState)=>{ const {data} =await Axios.get(`/api/products/${productId}`); dispatch({ type:CART_ADD_ITEM, payload:{ name:data.name, image:data.image, price:data.price, countInStock:data.countInStock, product:data._id, qty, } }); localStorage.setItem('cartItems', JSON.stringify(getState().cart.cartItems)); }
store.js에 가서 다음과 같이 작성해준다.
const initialState = { cart:{ cartItems: localStorage.getItem('cartItems')? JSON.parse(localStorage.getItem('cartItems')):[] } };
다시 랜딩페이지로 가도 사라지지 않은 것을 확인할 수 있다. 리프레시 해도 사라지지 않고 state가 남아있다. 이제 카트 상단부분에 카트에 몇개가 있는지 보이도록하는 기능을 추가해보자.
App.js에 다음과 같이 작성해준다.
import { useSelector } from 'react-redux'; function App() { const cart = useSelector(state=> state.cart); const {cartItems} = cart; return ( <div> <Link to="/cart">Cart {cartItems.length>0 &&( <span className="badge">{cartItems.length}</span> )} </Link> </header>
index.css로 가서 디자인해준다.
a.brand { color: #ffffff; font-size: 3rem; font-weight: bold; } .badge{ background-color: #f02020; color: white; border-radius: 50%; padding: 0.2rem 0.7rem; font-size: 1.4rem; margin-left: 0.2rem; }
'NODE.JS' 카테고리의 다른 글
아마존 E-commerce 클론 -10) MongoDB연결하고 user데이터 만들기 (1) 2021.05.27 아마존 E-commerce 클론 -9) CartScreen 만들고, removeFromCart버튼 활성화하기 (0) 2021.05.27 아마존 E-commerce 클론 -7) HomeScreen, ProductScreen에 리덕스 추가하기 (0) 2021.05.26 아마존 E-commerce 클론 -6) Node.js server 만들기 (0) 2021.05.26 아마존 E-commerce 클론 -5) Node.js server 만들기 (0) 2021.05.26