React-Redux 스무디 전자상거래 사이트
Flatiron의 소프트웨어 엔지니어링 부트캠프를 위한 마지막 React-Redux 프로젝트를 위해 가짜 스무디 가게를 위한 데모 전자 상거래 사이트를 만들었습니다. 주요 기능은 Build A Smoothie 및 카트 기능입니다. 이 블로그 게시물에서는 카트 기능을 구성하는 코드를 시연할 것입니다. 장바구니 기능을 빠르게 함께 사용하려는 모든 사람에게 도움이 되었으면 하는 매우 기본적인 쇼핑 카트입니다.
우선 장바구니를 5가지 구성 요소로 분류했습니다.
또한 내 작업을 처리하기 위해 redux 카트 리듀서를 만들고 내 redux 상태를 매핑하고 CartContainer라는 구성 요소에 작업을 발송했습니다. 내 CartContainer 내에서 redux state/dispatch 작업을 적절한 구성 요소에 전달했습니다.
import React, { Component } from 'react';
import CartList from '../components/cart/CartList'
import { connect } from 'react-redux'
import CartTotal from '../components/cart/CartTotal'
import CheckoutButton from '../components/cart/CheckoutButton'
import { Container } from 'react-bootstrap'
import { removeCartItem } from '../actions/cartActions.js'
class CartContainer extends Component {
render() {
return (
<Container>
<div className="body-wrapper">
<div className="inner-wrapper">
<div>
<CartList removeCartItem={this.props.removeCartItem} items={this.props.items} />
<CartTotal cartTotal={this.props.cartTotal} />
<CheckoutButton items={this.props.items} totalPrice={this.props.cartTotal} />
</div>
</div>
</div>
</Container>
);
}
}
const mapStateToProps = (state) => ({
items: state.cartReducer.cartItems,
ingredientIds: state.cartReducer.cartItems.ingredientIds,
cartTotal: state.cartReducer.cartTotal
})
const mapDispatchToProps = dispatch => ({
removeCartItem: (id) => dispatch(removeCartItem(id)),
})
export default connect(mapStateToProps, mapDispatchToProps)(CartContainer);
My
CartList
, CartItem
, CartTotal
및 CheckoutButton
는 단순히 내 글로벌 스토어의 버튼과 데이터를 렌더링합니다.내
CheckoutContainer
내에서 결제 정보를 수집하기 위해 매우 표준적인 HTML 양식을 만들었습니다. <form className="checkout-form">
<label>Please Enter Checkout Details</label><br></br>
<Row>
<br></br>
<Col>
<label htmlFor="customerName">* Name:</label><br></br>
<input id="customerName" type="text" name="customerName" onChange={this.handleChange} /><br></br>
<label htmlFor="address">* Address:</label><br></br>
<textarea id="address" type="textarea" name="address" onChange={this.handleChange} /><br></br>
<label htmlFor="note">Customer Note:</label><br></br>
<input id="note" type="text" name="note" onChange={this.handleChange} /><br></br>
</Col>
<Col>
<label htmlFor="cardnum">* Card Number:</label>
<input id="cardnum" type="text" name="cardNumber" onChange={this.handleChange} /><br></br>
<label htmlFor="cardexp">* Expiration Date (MM/YY):</label>
<input id="cardexp" type="text" name="cardExp" onChange={this.handleChange} /><br></br>
<label htmlFor="cardsec">* Security Code:</label>
<input id="cardsec" type="password" name="cardSecurityNum" onChange={this.handleChange} /><br></br>
<br></br>
<Button variant="success" onClick={(e) => this.handleSubmit(e)} >Submit Order</Button>
</Col>
</Row>
</form >
CheckoutForm
구성 요소는 궁극적으로 체크아웃 완료 시 데이터베이스에 게시되는 글로벌 redux 스토어로 전달하기 위해 자체 상태를 추적합니다. 카드정보는 가맹점이라 사실 진짜 카드정보를 수집하고 싶지 않아서 주석 처리합니다. state = {
items: this.props.items,
totalPrice: this.props.totalPrice,
customerName: '',
address: '',
// cardNumber: '',
// cardExp: '',
// cardSecurityNum: '',
note: '',
message: ''
}
결제 디스패치 기능을 내
CheckoutForm
컨테이너에 매핑하고 제출 이벤트 핸들러 내에서 활용했습니다.const mapDispatchToProps = dispatch => ({
checkout: (data) => dispatch(checkout(data)),
emptyCart: () => dispatch(emptyCart())
})
handleChange = (e) => {
this.setState((prevState) => ({
...prevState,
[e.target.name]: e.target.value,
}))
}
handleSubmit = (e) => {
e.preventDefault()
if (this.state.items.length > 0 && this.state.customerName !=='' && this.setState.address !== '') {
this.props.checkout(this.state)
this.props.emptyCart()
this.setState({message: "Your order is on the way!"})
} else {
this.setState({message: "Please fill out all required fields"})
}
}
체크아웃 POST 작업은 Thunk를 활용하여 내 디스패치를 통합합니다.
export const checkout = (data) => {
return (dispatch) => {
dispatch({type: 'LOADING_POST'})
return fetch('https://boiling-earth-59543.herokuapp.com/orders', {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(humps.decamelizeKeys({order: data}))
})
.then(resp => resp.json())
.then(json => {
console.log('this is the posted checkout obj', json)
dispatch({type: 'CHECKOUT', payload: data})
})
}
}
그리고 내 감속기는 이 체크아웃 항목을 글로벌 스토어에 저장하는 작업을 처리합니다.
export default function cartReducer(state= {
order: []}, action) {
switch (action.type) {
case 'CHECKOUT':
const newOrder = {
ingredientIds: action.payload.items,
totalPrice: action.payload.totalPrice,
customerName: action.payload.customerName,
address: action.payload.address,
cardNumber: action.payload.cardNumber,
cardExp: action.payload.cardExp,
cardSecurityNum: action.payload.cardSecurityNum,
note: action.payload.note
}
return {
order: [...state.order, newOrder]
}
default:
return state
}
}
내 백엔드에 관한 한 상황은 매우 간단합니다. 내 백엔드는 Rails API입니다. 카트 기능과 관련된 내 모델 관계는 매우 간단합니다.
class Order < ApplicationRecord
has_many :products
class Product < ApplicationRecord
has_many :product_ingredients
has_many :ingredients, through: :product_ingredients
belongs_to :order
주문과 연결해야 할 때까지 프론트엔드에서 스무디 제품을 생성할 때 데이터베이스에 추가 가져오기를 수행할 실질적인 이유가 없었기 때문에 주문과 동시에 제품을 데이터베이스에 저장합니다. 내 생성 방법은 다음과 같습니다.
def create
@order = Order.create(order_params)
Product.add_ingredients(params[:order][:items], @order)
if @order.save
render json: @order, status: :created, location: @order
else
render json: @order.errors, status: :unprocessable_entity
end
end
주문과 함께 Prodcuts를 만드는 데 도움이 되는 클래스 메서드
add_ingredients
를 만들었습니다. def self.add_ingredients(items, order)
items.each do |item|
product = Product.create
item[:ingredient_ids].each do |id|
ingredient = Ingredient.find_by(id: id)
product.ingredients << ingredient
product.order_id = order.id
product.save
end
end
end
그리고 그게 다야! 주문은 제품(스무디) 및 이를 구성하는 재료와 함께 데이터베이스에 유지됩니다. 읽어 주셔서 감사합니다!
Reference
이 문제에 관하여(React-Redux 스무디 전자상거래 사이트), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/alliecaton/empty-tbd-cl텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)