create-react-app cart
npm i redux react-redux redux-thunk
-
結(jié)構(gòu)如下
- 代碼如下
acions > index.js
import * as shop from '../api/shop'
export const reciveProducts = products => ({
type: 'RECIVE_PRODUCTS',
products
})
export const addToCart = product => ({
type: 'ADD_TO_CART',
product
})
export const getAllProducts = () => dispatch => {
shop.getAllProducts(products => {
dispatch(reciveProducts(products))
})
}
export const setCheckoutStatus = status => ({
type: 'SET_CHECKOUT_STATUS',
status
})
export const setCarItems = items => ({
type: 'SET_ITEMS',
items
})
export const checkout = (products) => dispatch => {
// 1.備份購物車數(shù)據(jù)羹与,結(jié)算失敗好返回
const tempCartPro = [...products]
// 2.清空結(jié)算狀態(tài)
dispatch(setCheckoutStatus(null))
// 3.清空購物車
dispatch(setCarItems([]))
// 4.執(zhí)行結(jié)算操作
shop.buyProducts(
products,
// 成功
() => dispatch(setCheckoutStatus('success')),
// 失敗
() => {
dispatch(setCheckoutStatus('fail'))
dispatch(setCarItems(tempCartPro))
}
)
}
api > shop.js
const _products = [
{"id": 1, "title": "9.9包郵華為手機(jī)", "price": 9.9, "inventory": 3},
{"id": 2, "title": "樂高積木馬里奧超級馬里奧周邊", "price": 1119.9, "inventory": 12},
{"id": 3, "title": "ps4荒野大鏢客不好玩免單", "price": 229.9, "inventory": 5},
]
// 模擬接口
export const getAllProducts = callback => {
setTimeout(() => {
callback(_products)
}, 1000);
}
export const buyProducts = (products, callback, errCallback) => {
setTimeout(() => {
Math.random() > 0.5 ? callback() : errCallback()
}, 500);
}
components > Cart.js
import React, { Component } from 'react'
class Cart extends Component {
render() {
const {cartProducts, totalPrice, checkout, checkoutStatus} = this.props
return (
<div>
<h2>Cart</h2>
<ul>
{cartProducts.map(item => (
<li key={item.id}>
{item.title}-{item.price}-{item.quantity}
</li>
))}
</ul>
{!cartProducts.length && <p>請?zhí)砑由唐返劫徫镘?lt;/p>}
<div>總價格是:{totalPrice}</div>
<br/>
<button
disabled={!cartProducts.length}
onClick={() => checkout(cartProducts)}>
結(jié)算
</button>
<br/>
{checkoutStatus && <div>結(jié)算信息{checkoutStatus}</div>}
</div>
)
}
}
export default Cart
components > Products.js
import React, { Component } from 'react'
class Products extends Component {
render() {
const { products } = this.props
return (
<div>
<h2>products</h2>
<ul>
{products.map(item => (
<li key={item.id}>
{item.title}-{item.price}-{item.inventory}
<br />
<button
disabled={!item.inventory}
onClick={() => this.props.addToCart(item)}>
{item.inventory ? '添加到購物車' : '庫存為0'}
</button>
</li>
))}
</ul>
</div>
)
}
componentDidMount() {
this.props.getAllProducts()
}
}
export default Products
container > CartContainer.js
import { connect } from 'react-redux'
import Cart from '../components/Cart'
import {checkout} from '../actions'
// 將products中的數(shù)據(jù)映射到cart中來
const getCartProducts = state => {
return state.cart.items.map( cartItem => {
const product = state.products.all.find(proItem => proItem.id === cartItem.id)
return {
id: product.id,
title: product.title,
price: product.price,
quantity: cartItem.quantity
}
})
}
// 獲取總價
const getTotalPrice = state => {
return getCartProducts(state).reduce((total, product) => {
return total + product.price * product.quantity
}, 0)
}
const mapStateToProps = state => {
return {
cartProducts: getCartProducts(state),
totalPrice: getTotalPrice(state),
checkoutStatus: state.cart.checkoutStatus
}
}
const mapDispatchToProps = {
checkout
}
const CartContainer = connect(
mapStateToProps,
mapDispatchToProps
) (Cart)
export default CartContainer
container > ProductsContainer.js
import { connect } from 'react-redux'
import Products from '../components/Products'
import { getAllProducts, addToCart } from '../actions'
const mapStateToProps = state => {
return {
products: state.products.all
}
}
const mapDispatchToProps = {
getAllProducts,
addToCart
}
const ProductsContainer = connect(
mapStateToProps,
mapDispatchToProps
) (Products)
export default ProductsContainer
reducers > cart.js
const initialState = {
items: [],
checkoutStatus: null
}
const items = (state = initialState.items, action) => {
switch (action.type) {
case 'ADD_TO_CART':
// 判斷購物車存在商品嗎
// 存在 - quantity+1
// 不存在 - 添加商品到購物車
const productId = action.product.id
const product = state.find(item => item.id === productId)
if (product) {
product.quantity++
return [...state]
} else {
return [...state, {
id: productId,
quantity: 1
}]
}
case 'SET_ITEMS':
return action.items
default:
return state
}
}
const checkoutStatus = (state = initialState.checkoutStatus, action) => {
switch (action.type) {
case 'SET_CHECKOUT_STATUS':
return action.status
default:
return state
}
}
export default (state = initialState, action) => {
return {
items: items(state.items, action),
checkoutStatus: checkoutStatus(state.checkoutStatus, action)
}
}
reducers > products.js
const initialState = {
all: []
}
const all = (state = initialState.all, action) => {
switch (action.type) {
case 'RECIVE_PRODUCTS':
return action.products
case 'ADD_TO_CART':
const productId = action.product.id
const product = state.find(item => item.id === productId)
product.inventory--
return [...state]
default:
return state
}
}
export default (state = initialState, action) => {
return {
all: all(state.all, action),
}
}
store > index.js
import { createStore, combineReducers, applyMiddleware, compose } from 'redux'
import thunk from 'redux-thunk'
import products from '../reducers/products'
import cart from '../reducers/cart'
const rootReducer = combineReducers({
products,
cart
})
const composeEnhancers =
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ?
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({}) : compose;
const enhancer = composeEnhancers(
applyMiddleware(thunk)
)
const store = createStore(
rootReducer,
// 啟用redux調(diào)試工具
enhancer
)
export default store
- 優(yōu)化與建議
action.type
里面的值是字符串恒傻,這樣每次使用dispatch
的時候荆忍,輸入的參數(shù)是字符串凰慈,容易出錯汛蝙。
解決辦法:
定義一個公用文件省咨,把action.type
保存進(jìn)去吼肥,每次使用的時候乌叶,將字符串的部分替換成對應(yīng)的變量即可跷敬。