Home > Mobile >  React Native Redux Component not rerendering on Redux State Change
React Native Redux Component not rerendering on Redux State Change

Time:07-13

I am working on a shopping basket component in React Native. The content and price of the basket get saved in the global redux store. When the user selects an item, an action gets dispatched to add the item to the basket and to update the total basket price.

The UI however does not get updated on this global state change.

My Reducer

const INITIAL_STATE = {
basket: [],
basketPrice: 0,
};

const mainReducer = (state = INITIAL_STATE, action) => {
    const stateCpy = state;
    switch (action.type) {
        case 'SET_BASKET':
            stateCpy.basket = action.payload
            return stateCpy;
        case 'ADD_TO_BASKET':
            stateCpy.basket.push(action.payload)
            return stateCpy
        case 'REMOVE_FROM_BASKET':
            let tempItems = stateCpy.basket
            for (var x = 0; x < stateCpy.basket.length; x  ) {
                if (stateCpy.basket[x]._id === action.payload) {
                    tempItems.splice(x, 1)
                    break;
                }
            }
            stateCpy.basket = tempItems
            return stateCpy
        case 'SET_BASKET_PRICE':
            stateCpy.basketPrice = action.payload
            console.log(stateCpy.basketPrice)
            return stateCpy
        default:
            return state
    }
};

export default mainReducer

My Actions

const setBasket = basket => ({
    type: 'SET_BASKET',
    payload: basket,
});
const addToBasket = item => ({
    type: 'ADD_TO_BASKET',
    payload: item,
});
const removeFromBasket = item_id => ({
    type: 'REMOVE_FROM_BASKET',
    payload: item_id,
});

const setBasketPrice = price => ({
    type: 'SET_BASKET_PRICE',
    payload: price,
});

export default actions = {
    setBasket,
    addToBasket,
    removeFromBasket,
    setBasketPrice
}

My UI Component

... 

import { useSelector, useDispatch } from 'react-redux'

export const RestaurantView = ({ navigation, route }) => {
    const basket = useSelector((state) => state.basket)
    const basketPrice = useSelector((state) => state.basketPrice)
    
    const dispatch = useDispatch()

...

function calcBasketPrice(){
        let tempBasketPrice = 0
        basket.forEach(element => {
            tempBasketPrice  = element.price
        });
        return tempBasketPrice
        
    }

    function addToBasket(item) {
        dispatch(actions.setBasketPrice(calcBasketPrice()   item.price))
        dispatch(actions.addToBasket(item))
    }


return ( <View>
    <ItemCard onPress={addToBasket}> </ItemCard>
    <Text style={{ textAlign: "right", padding: 15, fontSize: 20 }}> {basketPrice}</Text>
</View>)

}

When logging the basketPrice to console in the reducer, it logs the correct, updated value on each press/dispatch but there no changes in the UI. When a local state change is made to force a rerender, it renders with the correct value from the global store.

CodePudding user response:

Your stateCpy variable is actually not a copy, but just a reference to state - so your reducer is actually modifying the old redux state instead of creating a new one.

Since this is a very outdated style of Redux and in modern Redux createSlice reducers it is totally okay to modify state, I would recommend you not to fix this in legacy Redux, but to take a look at modern Redux - which is also less error-prone and about 1/4 of the code you would write with legacy Redux. Rewriting your reducer is not a lot of work and in the long run you will really benefit from the (since 2019) new style.

Take a look at the official Redux tutorial

  • Related