Search code examples
reactjsobjectwebstaterender

React Js State Updates


Functional Component React Making a pizza website where you can buy pizza online. On the website, as online shopping websies, there is cart where you can add products. There is an array which has objects as children. Each object is representing a "pizza" information: type, price, size, number.

When pizza added to "cart", the price[calculated by the original price * number of pizza] should be added to the object inside the array as well. I used a state for the price. However, when added to the pizza array, I find the price of the previous pizza, meaning the previous render.

setPrice(origPrice * numberOfPizza);
array.pizza.price = price;

When I wrote this next code, it got fixed.

setPrice(origPrice * numberOfPizza);
array.pizza.price = origPrice * numberOfPizza;

PS: this code is implemented in a useEffect, when the "number of pizza" or the "size" gets changed.

I don't understand why this happened.

Original Code If Needed:

const [numberOfPizza, setNumberOfPizza] = useState(properties.initialNumberOfPizza || 1);
const [sizeOfPizza, setSizeOfPizza] = useState(properties.sizeUnitLetter);
const [price, setPrice] = useState(0);
const pizzaSharedToCart = useContext(PizzaIdCartContext);

//when adding elements to the cart
const saveToCart = () => {
const origPrice = dataPizza.filter((singlPrice) => {
         return singlPrice.pizzaId == properties.pizzaId;
})[0];
const tochange = origPrice.price.filter((singPrice) => {
         return singPrice.unit == sizeOfPizza;
})[0].value;
        pizzaSharedToCart.setPizzaCart([{pizzaId:properties.pizzaId,keyId:uniqueId,numberPizza:numberOfPizza, sizePizza:sizeOfPizza, price:tochange * numberOfPizza}, ...pizzaSharedToCart.pizzaCart]);
}
const addPizza = (event) => {
     if(numberOfPizza == 10){
         event.preventDefault();
         return '';
     }
     setNumberOfPizza(prev => prev + 1);
}
const lessPizza = (event) => {
     if(numberOfPizza == 1){
         event.preventDefault();
         return '';
     }
     setNumberOfPizza(prev => prev - 1);
}
useEffect(()=>{

     // when changing the values in the cart
     if(!styles.display){
         const objPizza = dataPizza.filter((single) => {
             return single.pizzaId == properties.pizzaId;
         })[0];
         const origPrice = objPizza.price.filter((singlobjpr) => {
             return singlobjpr.unit == sizeOfPizza;
         })[0].value;
         setPrice(origPrice * numberOfPizza);
         let array = pizzaSharedToCart.pizzaCart;
         array[properties.eachIndex].numberPizza = numberOfPizza;
         array[properties.eachIndex].sizePizza = sizeOfPizza;
         array[properties.eachIndex].price = origPrice * numberOfPizza;
         pizzaSharedToCart.setPizzaCart(array);
     }
}, [sizeOfPizza, numberOfPizza]);

Solution


  • Inorder for a component rerender, its state should be changed.

    For basic numbers and string, it is easy to understand and visualize any change in its value will cause re-render.
    However, when it comes to map and arrays, change in value doesn't mean it will be re-render as both updated value and older value can point to same location.

    As per React's documentation

    Arrays are mutable in JavaScript, but you should treat them as immutable when you store them in state. Just like with objects, when you want to update an array stored in state, you need to create a new one (or make a copy of an existing one), and then set state to use the new array.

    Seems like this was the issue with your code, as earlier though values were changed it still pointed to same location.


    Consider cleaning up your code by creating temporary variables and then performing operations on them. You can clone the older values by using
    1. Loadash - _.deepClone(obj)
    2. JSON.parse(JSON.stringify(obj))

    This will help in developers understand when a component will re-render and when not (There's this use case too).