React Native unable to display updated setState value

Viewed 52

below is my code:

const ProductData = () => {
const [data, setData] = useState([])

const x = [{"product": {"product_description": "XYZ", "product_name": "Product1"}, "quantity": 1}]

setData(x);

function onChangeQuantity(i, type){
    let quant = data[i].quantity;
    if (type) {
      quant += 1;
      data[i].quantity = quant;
      setData(data);
      console.log(data);
    } else if (type === false && quant >= 2) {
      quant -= 1;
      data[i].quantity = quant;
      setData(data);
      console.log(data);
    } else if (type === false && quant === 1) {
      data.splice(i, 1);
      setData(data);
    }
}

return(
  <View>
    {data.map((item, index) => (
       <View key={index}>
         <Text>
            {item.product.product_name}
         </Text>
       </View>
       <View style={styles.container4}>
         <TouchableOpacity
            onPress={() => onChangeQuantity(index, false)}>
              <Text> Decrement Value </Text>
          </TouchableOpacity>
          <Text>{item.quantity}</Text>
          <TouchableOpacity
            onPress={() => onChangeQuantity(index, true)}>
              <Text> Increment Value </Text>
          </TouchableOpacity>
        </View>
      </View>      
))}
</View>
)
}

Now the issue am facing is - whenever I hit the Increment Value/ Decrement value the quantity is getting updated but the updated value is not getting displayed inside the return statement. Sample console log after Increment Value button is pressed is as follows:

[{"product": {"product_description": "Product2", "product_name": "Product2"}, "quantity": 2}]

But the change is not reflected in the View. How do I display the updated quantity immediately after the button is clicked. Please let me know where am I going wrong. Thanks a lot in advance.

2 Answers

Don't mutate state in React - it'll cause re-rendering problems. Update your function to an immutable version instead.

function onChangeQuantity(i, add) {
    if (add) {
        setData(
            data.map((item, j) => i === j ? ({ ...item, quantity: item.quantity + 1 }) : item)
        );
        return;
    }
    const curr = data[i];
    if (curr.quantity === 0) {
        setData(
            data.filter((_, j) => i !== j)
        );
    } else {
        setData(
            data.map((item, j) => i === j ? ({ ...item, quantity: item.quantity - 1 }) : item)
        );
    }
}

Another approach:

function onChangeQuantity(i, add) {
    const newObj = { ...data[i], quantity: data[i].quantity + (add ? 1 : -1) };
    setData(
        data
            .map((item, j) => i === j ? newObj : item)
            .filter(item => item.quantity)
    );
}

Issue

You are mutating the state object.

let quant = data[i].quantity; // <-- reference to state object
if (type) {
  quant += 1;
  data[i].quantity = quant; // <-- mutation!!
  setData(data); // <-- saved same array back to state
  console.log(data);
} else if (type === false && quant >= 2) {
  quant -= 1;
  data[i].quantity = quant; // <-- mutation!!
  setData(data); // <-- saved same array back to state
  console.log(data);
} else if (type === false && quant === 1) {
  data.splice(i, 1); // <-- mutation!!
  setData(data);
}

Solution

You need to shallow copy state. Copy the data array into a new array reference, and then shallow copy the element you are updating the quantity of.

function onChangeQuantity(i, type){
  if (type) {
    setData(data => data.map((el, index) => index === i ? {
      ...el,
      quantity: el.quantity + 1,
    } : el));
  } else if (!type && data[i] >= 2) {
    setData(data => data.map((el, index) => index === i ? {
      ...el,
      quantity: el.quantity - 1,
    } : el));
  } else if (!type && quant === 1) {
    setData(data => data.filter((el, index) => index !== i));
  }
}
Related