I have 2 states product and variations I call an API and set the values of both state to the API response.
I want the product state to stay as it is and not update
const [product, setProduct] = useState({} as any);
const [variations, setVariations] = useState([] as any);
useEffect(() => {
const getProduct = async () => {
const data = await axios.get("/products?id=4533843820679");
console.log(data);
setProduct(data.data);
// @ts-ignore
setVariations([data.data]);
};
getProduct();
}, []);
In return I map the variations array and return inputs for title, and price and a button to add variations. Adding variations will add another product to variations array. So it just pushes product to variations.
Then I have inputs for title in variation and prices in variation.variants. The problem is with onChange.
When I change the price of one element in variants it changes for all and also changes it for PRODUCT state.
The code can be found here: https://codesandbox.io/s/smoosh-firefly-6n747?file=/src/App.js
Add variations, change prices add another variations and you'll see all issues I'm facing.
It is because of this:
variant.price = e.target.value; // same issue with title
the variant object reference is shared among variations and you are modifying it directly. It is shared because you you made a shallow copy of a variation using ... when adding it.
Here is the solution:
You should update the specific variant object in immutable way (in react you should always update state in immutable way). For that you need to use this as onChange for price:
onChange = {
(e) => {
let updated = variations.map((x) => {
if (x.id === variation.id) {
return {
...x,
variants: x.variants.map((y) => {
if (y.id === variant.id) {
return {
...y,
price: e.target.value
};
}
return y;
})
};
}
return x;
});
setVariations(updated);
}
}
This for onChange for title:
onChange = {
(e) => {
let updated = variations.map((x) => {
if (x.id === variation.id) {
return {
...x,
title: e.target.value
};
}
return x;
});
setVariations(updated);
}
}
NOTE but ids of variations must be different. For testing purposes you can use this as click handler when adding a new variation:
onClick = {
() => {
setVariations((prev) => [...prev, {
...product,
id: Math.floor(Math.random() * 1000) // for testing
}]);
}
}
First, you are not pushing the product to variations. You are overwriting it.
To push a value to array with useState,
setVariations([...variations, product])
But, if you change the product object, variations also gonna be change because it's the same object. (Maybe, react not gonna re-render it but trust me, it is changed.) If you want to keep it same you need to create new object.
So,
setProduct(data.data);
setVariations([...variations, {...data.data}]);
Now, you can change product. variations not gonna change.
This was because you did a shallow copy of an object.
Try to do like this:
setVariations([...variations, data.data,]);