Пропадает компонент React при изменении состояния
Помогите, пожалуйста, разобраться, уже сутки не могу понять что к чему. Есть карточки товаров, при клике на кнопку в карточке, товар пушится в массив, из этого массива другой компонент(корзина) отрисовывает собственно тот товар, на который был совершен клик(добавить в корзину). То есть банальное добавление товаров в корзину, но, чтобы в корзину не добавлялось 2 одинаковых товара, я написал проверку и если такой товар уже присутствует в массиве, то в корзине должно меняться только цифра в строке "Количество"
let basketItem = [];
const [quantity, setQuantity] = useState(1);
const addItem = (item) => {
basketItem.includes(item) ? setQuantity(quantity + 1) : basketItem.push(item);
}
Пропсами всё передается в шапку, где находится компонент корзины
return (
<div className='app'>
<Header {...{
expand: 'md',
fixed: 'top',
color: 'light'
}}
cities={cities}
basketItem={basketItem}
quantity={quantity}
/>
Вот кнопка, по которой всё происходит
<Button onClick={() => props.addItem(item)}>
А вот код компонента Корзины
function Basket(props) {
const [isOpen, setIsOpen] = useState(false);
const toggle = () => setIsOpen(!isOpen)
let arrItems = props.basketItem;
console.log(props.quantity)
function renderItems(arr) {
const items = arr.map((item, i) => {
console.log(props.basketItem)
return (
<div
key={i}>
<Row>
<Col sm="12">
<Card body>
<CardTitle tag="h5">
{item.name} | Количество: {props.quantity}
</CardTitle>
<Button>
Go somewhere
</Button>
</Card>
</Col>
</Row>
</div>
)
});
return (
<>
{items}
</>
)
}
const itemsDone = renderItems(arrItems)
return (
<>
<img src={basket} className="basket" onClick={toggle} />
<Offcanvas toggle={toggle}
isOpen={isOpen}
direction="end"
scrollable>
<OffcanvasHeader toggle={toggle}>
Корзина
</OffcanvasHeader>
<OffcanvasBody>
{itemsDone}
</OffcanvasBody>
</Offcanvas>
</>
);
}
export default Basket;
Проблема в том, что если товар добавлен в Корзину, то повторный клик по "Добавить в корзину" удаляет из корзины все товары, но если кликнуть еще раз, то товары снова появляются с уже обновленным и актуальным стейтом(количество: 2) Вообще не понимаю почему так происходит, почему для нормального отображения на кнопку нужно кликать 2 раза. Буду очень признателен за подсказки.
Товар - это объект. Пример товара:
{
name: "shovel1",
top: 1,
img: "https://remitek.ru/files/2020/09_22/14_09/u_files_store_1_1373036.jpg",
type: "Bayonet",
},
Карточка товара:
<Card
style={{
width: '18rem'
}}
>
<img
alt="Sample"
src={item.img}
/>
<CardBody>
<CardTitle tag="h5">
{item.name}
</CardTitle>
<CardText>
Some quick example text to build on the card title and make up the bulk of the card‘s content.
</CardText>
<Button onClick={() => props.addItem(item)}>
Button
</Button>
</CardBody>
</Card>
Ответы (1 шт):
React перерисовывает компонент, только в том случае, если изменились переменные состояния или пропс компонента.
Так же, javaScript передает массивы и объекты по ссылкам и изменение содержимого не приводит к изменению ссылки, поэтому и React не видит изменения при push. Необходимо передавать новый массив и положить его в состояние компонента, что бы React его отслеживал
const [basketItem, setBasketItem] = useState([]);
const [quantity, setQuantity] = useState(1);
const addItem = (item) => {
if (basketItem.includes(item)) {
setQuantity(quantity + 1)
} else {
setBasketItem(prev => {
prev.push(item);
return [ ...prev ];
})
}
}
А на quantity реагировал, потому как первый клик - пуш элемента и ничего не происходит, второй клик, пуша нет, но есть изменение состояния компонента.
И я бы изменил формат хранения данных в корзине на массив объектов, вот так:
const [basketItem, setBasketItem] = useState([]);
const addItem = (item) => {
const elem = basketItem.find(i => i.name === item);
if (elem) {
elem.quantity += 1;
} else {
basketItem.push({
name: item,
quantity: 1
});
}
setBasketItem([ ...basketItem ]);
}
Теперь у вас каждый товар будет содержать информацию о названии и количестве в корзине
UPD после обновления вопроса:
т.к. товар это изначально объект, то можно при добавлении товара в корзину, добавлять поле quantity.
const [basketItem, setBasketItem] = useState([]);
const addItem = (item) => {
const elem = basketItem.find(i => i.name === item);
if (elem) {
elem.quantity += 1;
} else {
basketItem.push({ ...item, quantity: 1 });
}
setBasketItem([ ...basketItem ]);
}
В то же время, includes для объектов не подходит(конечно можно использовать для поиска одинаковых объектов, но в данном случае этого делать не стот)