0

I want to write a reducers-function that deletes an element of an array. The array looks like that:

Array [
  Object {
    "category": "First Course",
    "id": 0,
    "name": "Feta im Backofen gratiniert",
    "price": 9.8,
  },
  Object {
    "category": "First Course",
    "id": 1,
    "name": "Feta gebacken in der Kruste",
    "price": 9.8,
  },
  Object {
    "category": "First Course",
    "id": 2,
    "name": "Frischer Oktapus",
    "price": 9.8,
  }]

Here is my reducer class:

const initialState = {
    restaurantId:0,
    cartItems:[],
    tableNumber:0,
}

const reducers = (state = initialState, action) => {
    switch (action.type) {
        case 'UPDATE_ID':
            return {
                ...state,
                restaurantId: action.payload
            };
        case 'ADD_ITEM_TO_CART':
            return {
                ...state,
                cartItems:[...Object.assign([],{...state.cartItems}),action.payload.item]
            };
        case 'UPDATE_TABLE_NUMBER':
            return{
                tableNumber: action.payload
            };
        case 'REMOVE_ITEM_FROM_CART':
            console.log(action.payload,state.cartItems);
            return{
                ...state,
                cartItems: state.cartItems.splice(action.payload,1)

            }
    }
    return state
}

export default reducers;

When I'm calling the 'REMOVE_ITEM_FROM_CART'-case, there are different events that occure. Sometimes multiple array elements get removed and sometimes the right one gets removed. I don't know what's the problem here. I also console.log the action.payload which gives the index of the array element that I want to remove. It's always the correct payload.

EDIT: This is the component in which the action gets called:

 import React from "react";
    import { StatusBar } from "expo-status-bar";
    import { NavigationActions } from "react-navigation";
    import { ImageBackground, StyleSheet, View, Text, TextInput, TouchableOpacity } from "react-native";
    import ShoppingBag from "../components/ShoppingBag";
    import {connect} from 'react-redux';
    
    class ShoppingBagScreen extends React.Component {
    
        returnOptionsShoppingBag=()=>{
            if(this.props.cartItems.length===0 ||typeof this.props.cartItems.length===undefined) {
                    return(
                        <View>
                            <Text style={{fontSize:20}}>Ihr Warenkorb ist leer</Text>
                        </View>
                    )
                }else{
                return(
                    <View>
                <ShoppingBag shoppingBag={this.props.cartItems} onPress={this.props.deleteCartItem}/>
                <Text/>
                        {this.viewOrderButton()}
                    </View>
    
                )}
        };
    
        viewOrderButton(){
            if(this.props.tableNumber>0){
            return(
            <View>
            <TouchableOpacity >
                <Text style={{fontSize:20, color:'red'}}>Kostenpflichtig bestellen</Text>
            </TouchableOpacity>
            </View>
            )}else{
            return(
                <View>
                    <TouchableOpacity onPress={this.orderAlert}>
                    <Text style={{color:'red',opacity:0.3,fontSize:20}}>Kostenpflichtig bestellen</Text>
                    </TouchableOpacity>
                </View>
                )}
            };
    
        orderAlert(){
            return(
                alert('Bitte wählen Sie eine Tischnummer')
            )
        }
    
        render() {
            return (
                <View style={styles.Background}>
                    <Text style={{fontSize:20}}>Ihre Tischnummer lautet: {this.props.tableNumber}</Text>
                    <Text/>
                    {this.returnOptionsShoppingBag()}
                </View>
            )
        }
    
    
    }
    
    
    function mapDispatchToProps(dispatch){
    
        return {
            deleteCartItem: (index) => dispatch({type: 'REMOVE_ITEM_FROM_CART', payload: index})
        }}
    
    function mapStateToProps(state){
        return{
            cartItems: state.cartItems,
            tableNumber: state.tableNumber
            }
        }
    
    
    export default connect(mapStateToProps,mapDispatchToProps)(ShoppingBagScreen);
 

This is the ShoppingBag-component:

import React from 'react';
import {View,Text,TouchableOpacity} from 'react-native';
import Icon from 'react-native-vector-icons/FontAwesome';

export default class ShoppingBag extends React.Component{

    renderShoppingBag = (shoppingBag)=>{
        return shoppingBag.map((item,index)=>{
            return(
                <View key={index} style={{flexDirection:'row'}}>
                    <Text style={{fontSize:20}}> {item.name} {item.price}0€     </Text>
                    <TouchableOpacity onPress={()=>this.props.onPress(index)}>
                    <Icon name='trash' size={20}/>
                    </TouchableOpacity>
                </View>
            )
        })
    }

    render() {
        return(
            <View>
            {this.renderShoppingBag(this.props.shoppingBag)}
            </View>
        )
    }
}

Thanks in advance!

2
  • Without more details, I don't think we can help answer this. But this line looks very odd (in ADD_ITEM): cartItems:[...Object.assign([],{...state.cartItems}),action.payload.item]. I don't know your model, but wouldn't this work just as well?: cart: [...state.cartItems, action.payload.item]? Commented Oct 29, 2020 at 15:02
  • While it's commendable to add more context when asked for more information, I'm afraid you added the wrong context. The reducer function is easy enough to test. The trouble is that we don't have data to test with. What is in state and action when this fails? That's what we really need to know. Commented Oct 29, 2020 at 15:09

2 Answers 2

1

First, I'd suggest pulling your array action out of the return statement.

In fact, I highly recommend setting copies of your state objects and arrays as your first priority in each case.

case 'REMOVE_ITEM_FROM_CART':
  let cartAfterRemove = [...state.cartItems];
  cartAfterRemove.splice(action.payload, 1);
  //to confirm successful splice;
  console.log(cartAfterRemove);
  let newState = {...state, cartItems: cartAfterRemove};
  return newState;
}

redux can get really funky if you're not careful with nested items and having 0 mutation. You could also send the id of the object rather than the index, but that would entail more refactoring.

https://redux.js.org/recipes/structuring-reducers/immutable-update-patterns <- has some great examples.

Sign up to request clarification or add additional context in comments.

1 Comment

Thank you so much for your answer!!! It works fine! I think I need to go deeper into redux so thank you for the link as well.
0

Payload should be an index of the item because splice works on index not number of items to be deleted

5 Comments

But the payload is an index of the item.
@VincBue: That's not what you said in the question. Please update with the relevant state and action for a case that fails.
@VincBue you said action.payload gives the number of the array elements? that's why i thought payload should be an index of that item which gonna be remove
okay sorry guys. I've updated the question and inserted more information.
@NooruddinLakhani: Do you have an idea what's wrong?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.