create-react-app cart
npm i redux react-redux redux-thunk
-
结构如下
- 代码如下
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.备份购物车数据,结算失败好返回
const tempCartPro = [...products]
// 2.清空结算状态
dispatch(setCheckoutStatus(null))
// 3.清空购物车
dispatch(setCarItems([]))
// 4.执行结算操作
shop.buyProducts(
products,
// 成功
() => dispatch(setCheckoutStatus('success')),
// 失败
() => {
dispatch(setCheckoutStatus('fail'))
dispatch(setCarItems(tempCartPro))
}
)
}
api > shop.js
const _products = [
{"id": 1, "title": "9.9包邮华为手机", "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 (
Cart
{cartProducts.map(item => (
-
{item.title}-{item.price}-{item.quantity}
))}
{!cartProducts.length && 请添加商品到购物车
}
总价格是:{totalPrice}
{checkoutStatus && 结算信息{checkoutStatus}}
)
}
}
export default Cart
components > Products.js
import React, { Component } from 'react'
class Products extends Component {
render() {
const { products } = this.props
return (
products
{products.map(item => (
-
{item.title}-{item.price}-{item.inventory}
))}
)
}
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中的数据映射到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调试工具
enhancer
)
export default store
- 优化与建议
action.type
里面的值是字符串,这样每次使用dispatch
的时候,输入的参数是字符串,容易出错。
解决办法:
定义一个公用文件,把action.type
保存进去,每次使用的时候,将字符串的部分替换成对应的变量即可。