이번화에서는 product 상세 페이지에 대한 코드리뷰를 해볼 것이다.
: Product Detail은 option을 선택하는 컴포넌트와 장바구니 컴포넌트가 자식 컴포넌트로 존재한다.
: Product Detail의 부모 역할을 하는 컴포넌트이다. 지난번과 마찬가지로 함수부분을 제외하고는 생략하겠다ㅠㅠ
class Overview extends Component {
// 해당 제품에 대한 정보를 받아오는 함수만 컴디마에 넣었다.
componentDidMount() {
this.getProductList();
}
// 이게 위에서 말한 함수!!
getProductList = () => {
fetch(`${API}/store/${this.props.match.params.id}`, {
method: 'GET',
headers: {
authorization: localStorage.getItem('token'),
},
})
.then((res) => res.json())
.then((result) => {
this.setState({
productList: result.result[0],
lowestPrice: result.result[0].details[0].price,
coverImageSrc: result.result[0].product_image_url[0],
});
});
};
// 이건 북마크를 클릭했을때 마이페이지 서버에 포스트 하는 함수
postProductId = () => {
fetch(`${API}/user/bookmark`, {
method: 'POST',
body: JSON.stringify({
product_id: this.state.productList.product_id,
}),
headers: {
Authorization: localStorage.getItem('token'),
},
})
.then((res) => res.json())
.then((result) => {
console.log(result);
});
};
// 이건 장바구니에 넣었을때 장바구니 서버에 포스트 하는 함수
postCartInfo = (e) => {
if (this.state.selectedProducts.length) {
e.preventDefault();
fetch(`${API}/order/cart`, {
method: 'POST',
body: JSON.stringify(this.state.selectedProducts),
headers: {
Authorization: localStorage.getItem('token'),
},
})
.then((res) => res.json())
.then((result) => {});
this.props.takeModalEvent();
}
};
// color옵션을 선택시 작용하는 이벤트 함수
getSelectedProductColor = (e) => {
const { options, selectedIndex } = e.target;
const selectedColor = options[selectedIndex].innerHTML;
this.setState({ selectedColor });
};
// 사이즈 옵션을 선택시 작용하는 이벤트 함수
getSelectedProductOption = (e) => {
const { options, selectedIndex, value } = e.target;
const selectedProducts = [...this.state.selectedProducts];
if (
selectedProducts.some(
(selectedProduct) =>
selectedProduct.label === options[selectedIndex].innerHTML
) ||
selectedProducts.some((selectedProduct) => selectedProduct.color) ===
this.state.selectedColor
) {
alert('이미 선택한 옵션입니다.');
} else {
selectedProducts.push({
product_id: this.state.productList.product_id,
value: value,
count: 1,
color: this.state.selectedColor,
label: options[selectedIndex].innerHTML,
sale: this.state.sale,
seller: this.state.productList.product_seller,
});
this.setState({
selectedProducts,
});
}
};
// 사이즈 옵션 선택 후 수량을 선택시 작용하는 이벤트 함수
getProductCount = (targetProduct, countString) => {
const countNumber = parseInt(countString);
const selectedProducts = [...this.state.selectedProducts];
const index = selectedProducts.indexOf(targetProduct);
selectedProducts[index].count = countNumber;
this.setState({ selectedProducts });
};
// 옵션을 삭제하는 함수
handleDeleteProduct = (targetIndex) => {
const selectedProductsIndex = this.state.selectedProducts.filter(
(_, index) => index !== targetIndex
);
this.setState({ selectedProducts: selectedProductsIndex });
};
// 이미지 리스트에서 hover시 이미지를 변경하는 함수
changeCoverImage = (e) => {
this.setState({ coverImageSrc: e.target.src });
};
// 북마크에 따라 버튼이 바뀌는 이벤트 함수
handleBookmarkEvent = (e) => {
e.preventDefault();
this.setState({ bookMarkSwitch: !this.state.bookMarkSwitch });
this.postProductId();
};
}
const salePrice = Math.floor(lowestPrice - (lowestPrice * sale) / 100);
// 할인전 금액 부분
<div className='prevPrice'>
{Math.floor(lowestPrice && lowestPrice)
.toString()
.replace(/\B(?=(\d{3})+(?!\d))/g, ',')}
</div>
// 할인후 금액 부분
<div className='currentPriceBox'>
<div className='currentPrice'>
{salePrice
.toString()
.replace(/\B(?=(\d{3})+(?!\d))/g, ',')}
</div>
// 할부 금액 부분
<div className='interestFreePoint'>
{'월'}{' '}
{Math.floor((lowestPrice - lowestPrice / sale) / 7)
.toString()
.replace(/\B(?=(\d{3})+(?!\d))/g, ',')}{' '}
{'(7개월)'} {'무이자할부 >'}
</div>
<SelectOption
giveProductInfo={productList} //제품정보
giveSelectedProducts={selectedProducts} // 선택한 옵션 정보
giveBookmarkColor={bookMarkSwitch} // 북마크 스위치 정보
takeSelectedColor={getSelectedProductColor} // 선택한 컬러 이벤트
takeSelectedOption={getSelectedProductOption} // 선택한 옵션 이벤트
takeSelectedProductsValue={getProductCount} // 옵션 갯수 변경 이벤트
takeSelectedProductsDelIndex={handleDeleteProduct} // 옵션 삭제 이벤트
takeSelectedProductsCart={postCartInfo} /> // 장바구니 이벤트
takeBookmarkEvent={handleBookmarkEvent}
class SelectOption extends Component {
// 그저 이벤트 전달의 중간 통로(옵션갯수 고르는)
getProductCount = (targetProduct, countString) => {
this.props.takeSelectedProductsValue(targetProduct, countString);
};
// 그저 이벤트 전달의 중간 통로(옵션갯수 삭제하는)
handleDelete = (productIndex) => {
this.props.takeSelectedProductsDelIndex(productIndex);
};
}
getProductCount
에서는 이벤트만 보내는 것이 아니라 타겟 제품이 무엇인지, 갯수가 몇개인지까지 함께 전달하고 있다. 이것은 아래 SelectedProduct 컴포넌트
에서 전달하는 것이 무엇인지 보면 알 수 있다.// 색상을 고르는 select바
<select className='selectColorBox' onChange={takeSelectedColor}>
<option hidden selected>옵션: 색상</option>
{giveProductInfo.color && giveProductInfo.color.map((firstColorElement, firstOptionindex) => (
<option key={firstOptionindex} label={firstColorElement}{firstColorElement}
</option>))}
</select>
// 옵션을 고르는 select바
<select className='selectOptionBox' onChange={takeSelectedOption}>
<option hidden selected>옵션: 사이즈</option>
{giveProductInfo.details && giveProductInfo.details.map((firstOptionElement, firstOptionindex) => (
<option
key={firstOptionindex}
label={firstOptionElement.size}
value={firstOptionElement.price}>{firstOptionElement.size}
</option>))}
</select>
class SelectedProduct extends Component {
// 선택된 옵션이 여기서 최상위 부모로 전달되고 있었다.
getSelectedProduct = (e, targetProductInfo) => {
this.props.takeCountValue(targetProductInfo, e.target.value);
};
// 삭제 이벤트도 여기서 전달되고 있었다.
handleDeleteButton = (productIndex) => {
this.props.takeDeleteFuction(productIndex);
};
const counts = [1, 2, 3, 4, 5, 6, 7, 8, 9];
<select
value={targetProductInfo.count}
className='selectProductCount'
onChange={(e) => getSelectedProduct(e, targetProductInfo)}>
{counts.map((countNumber, countIndex) => (
<option key={countIndex} id={countIndex}>
{countNumber}
</option>))}
</select>
Cart와 Modal 등은 다음화에 계속!!
와....