How can I dispatch function in redux?

2020-01-29 03:00发布

问题:

I have a Cart array in the redux store that contains all the items I added it to cart

like this

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,
    },
  ],
};

and I have an input to add a coupon code, and when I added it should decrease the total price, so how can I achieve these if I didn't store a total in the store? and render the total price after adding this coupon!

here is my code snippet

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;


}

};

cart screen

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);

回答1:

My recommendation would be to not store redundant (derived) information in state. This means you don't need to (nor should you!) store the total in the state. The main reason this is a problem is that it gives your state an opportunity to contradict itself... if your state's total key doesn't equal the sum of the item totals, you've got a big problem!

Instead, you can create a function that calculates the total and simply call that when you need it. For example:

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;
}

And then, anywhere in your code you need to get the total, you can simple use this function. It's not uncommon to include this sort of function in mapStateToProps:

const mapStateToProps = state => ({
  total: calculateTotal(state);
});

If you end up with a lot of derived state, you may see some advantages from using a selector library like Reselect which can help you build selectors derived from other selectors and implements memoization for performance boosts.