我在redux存储中有一个Cart数组,其中包含我添加到购物车中的所有项目
像这样
const initialState = { cart: [ { product: { id: 1, name: 'Pizza cheese', price: 100, image: require('../../assets/food-3.png'), description: 'Tempor aute culpa ad voluptate aliquip ad ad laboris.', }, quantity: 3, }, { product: { id: 2, name: 'Steak meal', price: 200, image: require('../../assets/food-2.png'), description: 'Tempor aute culpa ad voluptate aliquip ad ad laboris.', }, quantity: 2, }, ], };
并且我有一个输入来添加优惠券代码,当我添加优惠券代码会降低总价,所以如果我没有在商店中存储总计,该如何实现?并在添加此优惠券后渲染总价!
这是我的代码片段
reducer / index.js
import { ADD_COUPON, ADD_TO_CART, MINUS_FROM_CART, PLUS_FROM_CART, REMOVE_ITEM, } from '../actions/types'; export default cartReducer = (state, action) => { console.log(action); switch (action.type) { case ADD_TO_CART: { return { cart: [ ...state.cart, { product: action.productInfo, quantity: action.quantity, }, ], }; } case PLUS_FROM_CART: { return { ...state, cart: state.cart.map(item => { if (item.product.id === action.productInfo.product.id) { return { ...item, quantity: action.quantity + 1, }; } return item; }), }; } case MINUS_FROM_CART: { return Object.assign({}, state, { cart: state.cart.map(item => { if (item.product.id === action.productInfo.product.id) { return Object.assign({}, item, { quantity: action.quantity <= 1 ? action.quantity : action.quantity - 1, }); } return item; }), }); } case REMOVE_ITEM: { return Object.assign({}, state, { cart: [ ...state.cart.filter( ({product}) => product.id !== action.productInfo.product.id, ), ], }); } case ADD_COUPON: { // here is console.log(state.cart); return { ...state, newTotal: state.cart.reduce( (total, item) => total + item.quantity * item.product.price, 0, ) - 50, }; } default: return state; } };
购物车屏幕
import React, {Component} from 'react'; import { Animated, Dimensions, FlatList, ScrollView, StyleSheet, Text, TextInput, TouchableOpacity, View, } from 'react-native'; import Swipeable from 'react-native-gesture-handler/Swipeable'; import Icon from 'react-native-vector-icons/Feather'; import {connect} from 'react-redux'; import CartIcon from '../components/CartIcon'; import CartItem from '../components/CartItem'; import { MinusFromCart, plusFromCart, removeItem, } from '../store/actions/actions'; class CartsScreen extends Component { state = { delivery: 15, total: this.props.total, coupon: '', }; applayCoupon = () => { // here is const {coupon} = this.state; if (coupon == 'Free') { this.props.addCoupon(); } }; handleIncreaseQuantity = (product, quantity) => { this.props.increaseQuantity(product, quantity); }; handleDecreaseQuantity = (product, quantity) => { this.props.decreaseQuantity(product, quantity); }; handleRemoveItem = product => { this.props.removeProduct(product); }; render() { return ( <View style={styles.container}> <View style={{marginBottom: 5}}> <View style={{ borderColor: '#d7d7d7', margin: 15, borderWidth: 1, padding: 15, borderRadius: 10, flexDirection: 'row', justifyContent: 'space-between', }}> <TextInput value={this.state.coupon} style={{width: '80%'}} onChangeText={coupon => this.setState({coupon})} placeholder="Write coupon code" /> <TouchableOpacity onPress={() => this.applayCoupon()}> <Text style={{color: '#f00'}}>Apply</Text> </TouchableOpacity> </View> <View style={{ paddingVertical: 5, marginBottom: 5, flexDirection: 'row', justifyContent: 'space-between', paddingHorizontal: 15, }}> <Text style={{fontSize: 15}}>Subtotal</Text> <Text style={{fontSize: 15}}>{this.props.total.toFixed(2)}$</Text> </View> <View style={{ paddingVertical: 5, marginBottom: 5, flexDirection: 'row', justifyContent: 'space-between', paddingHorizontal: 15, }}> <Text style={{fontSize: 15}}>Delivery Fee</Text> <Text style={{fontSize: 15}}>{this.state.delivery}$</Text> </View> <View style={{ borderTopColor: '#ddd', borderTopWidth: 1, paddingVertical: 10, alignSelf: 'center', }}> <View style={{ paddingVertical: 5, marginBottom: 5, flexDirection: 'row', justifyContent: 'space-between', paddingHorizontal: 15, }}> <Text style={{fontWeight: '700', fontSize: 18}}>Total</Text> <Text style={{fontWeight: '700', fontSize: 18}}> {this.props.total + this.state.delivery} </Text> </View> <Text style={{fontWeight: '700', paddingHorizontal: 15, fontSize: 18}}> after: {this.props.newTotal} $ </Text> <TouchableOpacity style={{ justifyContent: 'center', alignItems: 'center', padding: 15, borderRadius: 10, width: width - 20, backgroundColor: '#f10', }}> <Text style={{fontSize: 15, color: '#fff'}}>Checkout</Text> </TouchableOpacity> </View> </View> </View> ); } } const mapStateToProps = state => { return { cartItem: state.cart, total: state.cart.reduce( (total, item) => total + item.quantity * item.product.price, 0, ), newTotal: state.newTotal }; }; const mapDispatchToProps = dispatch => { return { increaseQuantity: (product, quantity) => dispatch(plusFromCart(product, quantity)), decreaseQuantity: (product, quantity) => dispatch(MinusFromCart(product, quantity)), removeProduct: product => dispatch(removeItem(product)), addCoupon: () => { dispatch({type: 'ADD_COUPON'}); }, }; }; export default connect( mapStateToProps, mapDispatchToProps, )(CartsScreen);
我的建议是不要在状态中存储冗余(派生的)信息。这意味着您不需要(也不应该!)将总数存储在状态中。这是一个问题的主要原因是,它给您的州提供了一个自相矛盾的机会……如果您州的total密钥不等于项目总数的总和,那么您将有一个大问题!
total
相反,您可以创建一个计算总数的函数,并在需要时简单地调用它。例如:
const calculateTotal = state => { let total = state.cart.reduce((total, item) => { return total + item.quantity * item.product.price }, 0); // Assuming you just have one coupon, you could maintain a boolean in state if (state.hasCoupon) { total -= 50; } return total; }
然后,在代码中需要获取总数的任何位置,都可以简单地使用此函数。在中包含这种功能并不罕见mapStateToProps:
mapStateToProps
const mapStateToProps = state => ({ total: calculateTotal(state); });
如果最终遇到很多派生状态,则使用诸如Reselect之类的选择器库可能会带来一些好处,该选择器库可以帮助您构建从其他选择器派生的选择器,并实现 备忘录 以提高性能。