React Native Redux Shopping Cart App Problems

Viewed 475

I am trying to make an application that allows the user to select 'Add to cart' within my DetailsComponent.js page. I am currently updating an array within my store named 'cart' with ids that correspond to products within a products array within the store. I have checked the debugger and the product id is correctly being added to the cart array. I am using the following code to show products on my cart page that match the product ids in my cart but my cart is currently showing nothing.

CartComponent.js

import React, { Component } from 'react';
import { FlatList, View, Text, Alert } from 'react-native';
import { ListItem } from 'react-native-elements';
import { connect } from 'react-redux';
import { baseUrl } from '../shared/baseUrl';
import { Loading } from './LoadingComponent';
import { removeCart } from '../redux/ActionCreators';


const mapStateToProps = state => {
    return {
        products: state.products,
        cart: state.cart
    }
}

const mapDispatchToProps =  dispatch => ({
    removeCart: (id) => dispatch(removeCart(id))
});

class CartScreen extends Component {
    
    render() {

        const renderMenuItem = ({item, index}) => {
            return(
               <ListItem 
                    key={index}    
                    bottomDivider
                >
                   <ListItem.Content>
                        <ListItem.Title>
                            {item.name}
                        </ListItem.Title>
                        <ListItem.Subtitle>
                            {item.quantity} chargers: ${item.price}
                        </ListItem.Subtitle>
                   </ListItem.Content>
                   
               </ListItem>
            );
        }
        if (this.props.cart.isLoading) {
            return(
                <Loading />
            )
        }
        else if (this.props.cart.errMess) {
            return(
                <Text>{this.props.cart.errMess}</Text>
            )
        }
        else {
            return(
            <View>
                <Text>
                    Cart
                </Text>
                <FlatList
                    data={this.props.products.products.filter(product => this.props.cart.cart.some(el => el === product.id))}
                    renderItem={renderMenuItem}
                    keyExtractor={item => item.id.toString()}
                />
            </View>
            );
        }
    }
}
export default connect(mapStateToProps, mapDispatchToProps)(CartScreen);

I have included my other files for more information.

I have different DropDownPicker values that correspond to the product IDs in my redux store. These items in the store have quantity and price values to be used in the CartComponent.js.

DetailsComponent.js

import React, { Component } from 'react';
import { Text, View, Image, Button, FlatList, StyleSheet, ScrollView} from 'react-native';
import DropDownPicker from 'react-native-dropdown-picker';
import { baseUrl } from '../shared/baseUrl';
import { connect } from 'react-redux';
import { Loading } from './LoadingComponent';
import { postCart } from '../redux/ActionCreators';


const mapStateToProps = state => {
    return{
        chargers: state.chargers,
        utensils: state.utensils,
        orders: state.orders,
        products: state.products
    }
}

const mapDispatchToProps = dispatch => ({
    postCart: (id) => dispatch(postCart(id))
})

class DetailsScreen extends Component {
    constructor(props) {
        super();
        this.state = {
            itemId: '',
            orderAmount: '',
            orderPrice: ''
        }
    }

    addToCart(id) {
        this.props.postCart(id);
    }

    render() {
        const categoryName = this.props.route.params.categoryName;
        const productId = this.props.route.params.menuId;                  
        const item = this.props[categoryName][categoryName][productId];
        if (categoryName === "chargers") {
            if (productId === 0) {
                var amounts = [
                    {label: '50', value: '1'},
                    {label: '100', value: '2'},
                    {label: '150', value: '3'},
                    {label: '200', value: '4'},
                    {label: '250', value: '5'},
                    {label: '300', value: '6'},
                    {label: '350', value: '7'},
                    {label: '400', value: '8'},
                    {label: '450', value: '9'},
                    {label: '500', value: '10'},
                    {label: '550', value: '11'},
                    {label: '600', value: '12'},
                ];
            }
            else if (productId === 1) {
                var amounts = [
                    {label: '50', value: '14'},
                    {label: '100', value: '15'},
                    {label: '150', value: '16'},
                    {label: '200', value: '17'},
                    {label: '250', value: '18'},
                    {label: '300', value: '19'},
                    {label: '350', value: '20'},
                    {label: '400', value: '21'},
                    {label: '450', value: '22'},
                    {label: '500', value: '23'},
                    {label: '550', value: '24'},
                    {label: '600', value: '25'},
                ];
            }
            else if (productId === 2) {
                var amounts = [
                    {label: '50', value: '27'},
                    {label: '100', value: '28'},
                    {label: '150', value: '29'},
                    {label: '200', value: '30'},
                    {label: '250', value: '31'},
                    {label: '300', value: '32'},
                    {label: '350', value: '33'},
                    {label: '400', value: '34'},
                    {label: '450', value: '35'},
                    {label: '500', value: '36'},
                    {label: '550', value: '37'},
                    {label: '600', value: '38'},
                ];
            }
        }
        else if (categoryName === "utensils") {
            if (productId === 0) {
                var amounts = [
                    {label: '1', value: '40'},
                    {label: '2', value: '41'},
                    {label: '3', value: '42'},
                    {label: '4', value: '43'},
                    {label: '5', value: '44'},
                ];
            }
            else if (productId === 1) {
                var amounts = [
                    {label: '1', value: '46'},
                    {label: '2', value: '47'},
                    {label: '3', value: '48'},
                    {label: '4', value: '49'},
                    {label: '5', value: '50'},
                ];
            }
                
        }
        
        
        if (this.props[categoryName].isLoading) {
            return(
                <Loading />
            )
        }
        else if (this.props[categoryName].errMess) {
            return(
                <Text>
                    {this.props[categoryName][categoryName].errMess}
                </Text>
            )
        }
        else {
            return(
                <ScrollView>
                    <Text style={styles.title}>
                        {item.name}
                    </Text>
                    <Text>
                        {item.category}
                    </Text>
                    <View style={{flex: 1, flexDirection: 'row'}}>
                        <Image
                            style={styles.image}
                            source={{uri: baseUrl + item.image}}
                        />
                        <DropDownPicker
                            items={amounts}
                            defaultNull
                            placeholder="Select amount"
                            containerStyle={{height: 40, width: 100}}
                            itemStyle={{
                                justifyContent: 'flex-start'
                            }}
                            onChangeItem={item => this.setState({
                                itemId: this.props.products.products[item.value].id,
                                orderAmount: this.props.products.products[item.value].quantity,
                                orderPrice: this.props.products.products[item.value].price
                            })}
                        />
                    </View>
                    
                    <Text>
                        {item.description}
                    </Text>
                    
                    <Text>
                        Your order is {this.state.orderAmount} {this.props[categoryName][categoryName][productId].name} chargers for ${this.state.orderPrice}
                    </Text>
                    <Button
                        title='Add to Cart'
                        color="#f194ff"
                        onPress={() => this.addToCart(this.state.itemId)}
                    />
    
                </ScrollView>
            )
        }
        
    }
}

const styles = StyleSheet.create({
    container: {
      flex: 1,
    },
    image: {
        resizeMode: "contain",
          height: 200,
          width: 200
    },
    title: {
      fontSize: 32,
    },
  });

export default connect(mapStateToProps, mapDispatchToProps)(DetailsScreen);

The following code samples are my redux files. I have simplified them with only the cart information for readability.

ActionCreators.js

export const fetchCart = () => (dispatch) => {
    
    dispatch(cartLoading());

    return fetch(baseUrl + 'cart')
    .then(response => {
        if (response.ok) {
            return response;
        } else {
            var error = new Error('Error ' + response.status + ': ' + response.statusText);
            error.response = response;
            throw error;
        }
        },
        error => {
            var errmess = new Error(error.message);
            throw errmess;
        })
    .then(response => response.json())
    .then(cart => dispatch(addCart(cart)))
    .catch(error => dispatch(cartFailed(error.message)));
};

export const cartLoading = () => ({
    type: ActionTypes.CART_LOADING
});

export const cartFailed = (errmess) => ({
    type: ActionTypes.CART_FAILED,
    payload: errmess
});

export const postCart = (id) => (dispatch) => {
    const newCart = {
        id: id
    };
    setTimeout(() => {
        dispatch(addToCart(newCart));
    }, 2000);
};

export const addToCart = (cart) => ({
    type: ActionTypes.ADD_TO_CART,
    payload: cart
});

export const addCart = (id) => ({
    type: ActionTypes.ADD_CART,
    payload: id
});

export const removeCart = (id) => ({
    type: ActionTypes.REMOVE_CART,
    payload: id
});

ActionTypes.js

export const POST_CART = 'POST_CART';
export const ADD_TO_CART = 'ADD_TO_CART';
export const ADD_CART = 'ADD_CART';
export const REMOVE_CART = 'REMOVE_CART';
export const CART_LOADING = 'CART_LOADING';
export const CART_FAILED = 'CART_FAILED';

cart.js

import * as ActionTypes from './ActionTypes';

export const cart = (
    state = { 
        isLoading: true,
        errMess: null,
        cart:[]
    }, 
    action) => {
        switch (action.type) {
            case ActionTypes.ADD_CART:
                return {...state, isLoading: false, errMess: null, cart: action.payload};
    
            case ActionTypes.CART_LOADING:
                return {...state, isLoading: true, errMess: null, cart: []};
    
            case ActionTypes.CART_FAILED:
                return {...state, isLoading: false, errMess: action.payload};

            case ActionTypes.ADD_TO_CART:
                var newCart = action.payload;
                return {...state, cart: state.cart.concat(newCart) };
    
            default:
                return state;
        }
    };

The following is my db.json file. I have input one item into the cart array for testing but it also does not show up in the cart. Have also simplified this file to only show a few products for readability.

"products": [
        {
            "id": 0,
            "name": "Ezywhip Pro",
            "category": "chargers",
            "label": "",
            "featured": false,
            "description": "Ezywhip Pro Cream Chargers, Made by MOSA",
            "image": "images/ezywhip.png",
            "quantity": 0,
            "price": 0
        },
        {
            "id": 1,
            "name": "Ezywhip Pro",
            "category": "ezy",
            "label": "",
            "featured": false,
            "description": "Ezywhip Pro Cream Chargers, Made by MOSA",
            "image": "images/ezywhip.png",
            "quantity": 50,
            "price": 40
        },
        {
            "id": 2,
            "name": "Ezywhip Pro",
            "category": "ezy",
            "label": "",
            "featured": false,
            "description": "Ezywhip Pro Cream Chargers, Made by MOSA",
            "image": "images/ezywhip.png",
            "quantity": 100,
            "price": 70
        },
        {
            "id": 3,
            "name": "Ezywhip Pro",
            "category": "ezy",
            "label": "",
            "featured": false,
            "description": "Ezywhip Pro Cream Chargers, Made by MOSA",
            "image": "images/ezywhip.png",
            "quantity": 150,
            "price": 110
        }
    ],
    "cart": [
        {
            "id": 1
        }
    ]
}

If anyone could explain what I'm doing wrong it would be greatly appreciated.

1 Answers

I got it working by changing the following:

export const addToCart = (id) => ({
    type: ActionTypes.ADD_TO_CART,
    payload: id
});

Not sure if this was causing any issues but I changed it to id for safe measure.

This is what was causing the main issue, I needed to use el.id to specify that's what I was comparing with in the data.

<FlatList
  data={this.props.products.products.filter(product => this.props.carts.carts.some(el => el.id === product.id))}
  renderItem={renderMenuItem}
  keyExtractor={item => item.id.toString()}
/>
Related