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