#javascript #reactjs #react-hooks
Вопрос:
У меня есть 2 состояния продукта и вариации, которые я вызываю API и устанавливаю значения обоих состояний в ответе API.
Я хочу, чтобы состояние продукта оставалось таким, как оно есть, и не обновлялось
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();
}, []);
Взамен я сопоставляю массив вариантов и возвращаю входные данные для названия, цены и кнопки для добавления вариантов. Добавление вариаций добавит еще один продукт в массив вариаций. Так что это просто подталкивает продукт к вариациям.
Затем у меня есть входные данные для названия в вариациях и цены в вариациях.варианты. Проблема в onChange.
Когда я изменяю цену одного элемента в вариантах, она меняется для всех, а также изменяет ее для состояния ПРОДУКТА.
Код можно найти здесь: https://codesandbox.io/s/smoosh-firefly-6n747?file=/src/App.js
Добавьте варианты, измените цены добавьте еще варианты, и вы увидите все проблемы, с которыми я сталкиваюсь.
Комментарии:
1. В вашем примере CodeSandbox вы не копируете продукт глубоко, поэтому
variants
массив разделяется между объектами.
Ответ №1:
Именно из — за этого:
variant.price = e.target.value; // same issue with title
variant
ссылка на объект является общей для разных вариантов, и вы изменяете ее напрямую. Он является общим, потому что вы сделали неглубокую копию variation
, используя ...
его при добавлении.
Вот решение:
Вы должны обновить конкретный variant
объект неизменяемым способом (в react вы всегда должны обновлять состояние неизменяемым способом). Для этого вам нужно использовать это как onChange
для 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);
}
}
Это для onChange
для title
:
onChange = {
(e) => {
let updated = variations.map((x) => {
if (x.id === variation.id) {
return {
...x,
title: e.target.value
};
}
return x;
});
setVariations(updated);
}
}
ОБРАТИТЕ внимание, но идентификаторы вариантов должны быть разными. В целях тестирования вы можете использовать это в качестве обработчика щелчка при добавлении нового варианта:
onClick = {
() => {
setVariations((prev) => [...prev, {
...product,
id: Math.floor(Math.random() * 1000) // for testing
}]);
}
}
Комментарии:
1. Работает отлично! Спасибо!
Ответ №2:
Во-первых, вы не подталкиваете продукт к изменениям. Вы его переписываете.
Чтобы переместить значение в массив с помощью useState,
setVariations([...variations, product])
Но если вы измените объект продукта, то также будут изменения, потому что это один и тот же объект. (Может быть, react не собирается перерисовывать его, но поверьте мне, он изменился.) Если вы хотите сохранить его прежним, вам нужно создать новый объект.
Так,
setProduct(data.data);
setVariations([...variations, {...data.data}]);
Теперь вы можете изменить продукт. вариации не изменятся.
Ответ №3:
Это произошло потому, что вы сделали неглубокую копию объекта.
Попробуй сделать вот так:
setVariations([...variations, data.data,]);