jzg
jzg

Reputation: 25

Redux state not updating even though reducer is called

I having a restaurant type application that I'm building. I'm using redux for handling the state. I have an icon in the top corner that keeps track of the number of items in the cart. This worked and was updated properly when the state contained an array. I have since change the state to a Map just for my own personal reasons and everything works EXCEPT the number is no longer being updated. I can see that the reducer is still doing the work however the number isn't updating like before. I've tried to look for error and still cannot find where it is going wrong.

My reducer:

import { MenuAction } from "../components/Utils";

const CartItems = (state : Map<Item, number> = new Map(), action: MenuAction) : Map<Item, number>  => {
    console.warn(state);
    switch (action.type) {
        case 'ADD_TO_CART':
            if (state.has(action.payload)) {
                return state.set(action.payload, state.get(action.payload) + 1);
            } else {
                return state.set(action.payload, 1);
            }
        case 'REMOVE_FROM_CART':
            if (state.has(action.payload)) {
                if (state.get(action.payload) == 1) {
                    state.delete(action.payload);
                    return state;
                } else {
                    return state.set(action.payload, state.get(action.payload) - 1);
                }
            }
    }
    return state
}

export default CartItems


The component with the icon that displays the number:


const ShoppingCartIcon = (props: any) => (
    <View style={[{ padding: 5 }, Platform.OS == 'android' ? styles.iconContainer : null]}>
        <View>
            <Text style={{color: 'white', fontWeight: 'bold'}}>
                {Utils.getCartTotal(props.cartItems)}
            </Text>
        </View>
        <Icon onPress={() => props.navigation.navigate('Cart')} name="ios-cart" size={30} />
    </View> 
)

const mapStateToProps = (state: Map<Item, number>) => {
    return {
        cartItems: state
    }
}

export default connect(mapStateToProps)(withNavigation(ShoppingCartIcon));

Upvotes: 1

Views: 2070

Answers (1)

Cat_Enthusiast
Cat_Enthusiast

Reputation: 15708

The problem is that you're doing a state-mutation which is against Redux principles. Although the state values appear to be updated in your proceeding code, the changes were being made to the same, initial object in reference. That is the problem with using new Map() as your initial state, you end up using methods that mutate state like .set():

state.set(action.payload, state.get(action.payload) + 1)

Redux stresses the concept of immutability. https://redux.js.org/recipes/structuring-reducers/immutable-update-patterns. As in do not make alterations to state, because it does not register as new data - so it finds no need to re-render your carts component with the updated numbers. To get your connected-component to re-render we need a completely new redux-state.

To achieve your desired outcome, you should revert it back to a simple array [] and use methods like .map() and .filter() that helps you create a brand-new copy of state.

Upvotes: 1

Related Questions