Как передать массив объектов из одного компонента в другой в React?
Только - только начал изучать React и столкнулся с проблемой: в приложении (что-то то вроде одностраничного интернет-магазина) при выборе товаров, они складываются в массив объектов "cartItems", который мне из "App" надо передать в "Modal", чтобы отобразить список товаров перед оформлением заказа, но никак не могу понять как это сделать, буду благодарен любой помощи. Прошу прощения за костыли и плохо читаемый код. Код "App.jsx":
import { useState, useEffect } from "react";
import "./App.css";
import Button from "./Components/Button/Button";
import Card from "./Components/Card/Card";
import Cart from "./Components/Cart/Cart";
import Modal from "./Components/Modal/Modal";
<script src="https://telegram.org/js/telegram-web-app.js"></script>
const { getData } = require("./db/db");
const foods = getData();
const tg = window.Telegram.WebApp;
function App() {
const [cartItems, setCartItems] = useState([]);
const [modalActive, setModalActive] = useState(false);
useEffect(()=>{
tg.ready();
})
const onAdd = (food) => {
const exist = cartItems.find((x) => x.id === food.id);
if (exist) {
setCartItems(
cartItems.map((x) =>
x.id === food.id ? { ...exist, quantity: exist.quantity + 1 } : x
)
);
} else {
setCartItems([...cartItems, { ...food, quantity: 1 }]);
}
};
const onRemove = (food) => {
const exist = cartItems.find((x) => x.id === food.id);
if (exist.quantity === 1) {
setCartItems(cartItems.filter((x) => x.id !== food.id));
} else {
setCartItems(
cartItems.map((x) =>
x.id === food.id ? { ...exist, quantity: exist.quantity - 1 } : x
)
);
}
};
const onCheckout = () => {
let pz = '{';
for (let i = 0; i < cartItems.length; i++ )
{
if (i != cartItems.length - 1)
{
if (cartItems[i].quantity === 1)
{
pz+=('\'' + cartItems[i].title + '\'' +' ' + ':' + ' ' + cartItems[i].price + '00' + ',');
}
if (cartItems[i].quantity != 1)
{
pz+=('\'' + cartItems[i].quantity + 'x' + ' ' + cartItems[i].title + '\'' + ' ' + ':' + ' ' + cartItems[i].price * cartItems[i].quantity + '00' + ',');
}
}
else
{
if (cartItems[i].quantity === 1)
{
pz+=('\'' + cartItems[i].title + '\'' + ' ' + ':' + ' ' + cartItems[i].price + '00' + '}');
}
if (cartItems[i].quantity != 1)
{
pz+=('\'' + cartItems[i].quantity + 'x' + ' ' + cartItems[i].title + '\'' + ' ' + ':' + ' ' + cartItems[i].price * cartItems[i].quantity + '00' + '}');
}
}
}
console.log(pz.toString());
console.log(cartItems);
setModalActive(true);
tg.MainButton.text = "Оформление";
tg.MainButton.show();
tg.onEvent('mainButtonClicked', function(){
tg.sendData(pz.toString());
});
}
return(
<>
<Modal active={modalActive} setActive={setModalActive}/>
<div className="shapka">
<div className="shapka_top"></div>
<h1 className="heading">Каталог</h1>
<Cart cartItems={cartItems} onCheckout={onCheckout} />
</div>
<div className="cards__container">
{foods.map((food) => {
return <Card food={food} key={food.id} onAdd={onAdd} onRemove={onRemove} />
} )}
</div>
</>
);
}
export default App;
Код "Modal.jsx":
import React from "react";
import { useState } from "react";
import "./Modal.css";
const Modal = ({active, setActive, inCart}) => {
return (
<div className={active ? "modal active" : "modal"}>
<div className="shapka_modal">
<h1 className="heading_modal">Ваш заказ</h1>
</div>
<h1>{inCart}</h1>
</div>
);
};
export default Modal;
Ответы (1 шт):
Можно воспользоваться context-ом, который позволят передвать значения от любой компоненты в любой другой, без сложностей
- Объявляем контекст
const ItemsContext= createContext() - Оборачиваем элементы, которые будут использовать этот контекст в тег
ItemsContext.Provider - Объявляем значение как объект, в котором 1 поле для значения цвета и 1 метод для обновления массива
value={{items, setItems}} - В любых дочерних компонентах достаём поле или метод в зависимости от того, кому что надо. Например кнопке не важно знать, какой сейчас массив, его дело - просто добавить нужный элемент, потому он использует только метод обновления массива
const {setItems} = useContext(ItemsContext). Самому блоку же, плевать кто и как обновлят массив, ему важно знать только нынешний массив, потому использует только поле, где значение массиваconst {items} = useContext(ItemsContext)
const {useState, useContext, createContext} = React; const ItemsContext = createContext(); const Item = ({pushValue}) => { const {setItems} = useContext(ItemsContext); return ( <button onClick={() => setItems((items) => [...items, pushValue])}>{pushValue.name}</button> ) } const Items = () => { return ( <React.Fragment> <Item pushValue={{id: 1, name: 'Item 1'}} /> <Item pushValue={{id: 2, name: 'Item 2'}} /> <Item pushValue={{id: 3, name: 'Item 3'}} /> </React.Fragment> ) } const Modal = () => { const {items} = useContext(ItemsContext); return ( <div className='block'>{items.map(item => item.name)}</div> ) } const App = () => { const [items, setItems] = useState([1]); return ( <ItemsContext.Provider value={{items, setItems}}> <Items /> <Modal /> </ItemsContext.Provider> ) }; ReactDOM.render( <App />, document.getElementById('root') )body { background-color: #777; } .block { margin-top: 8px; }<div id="root"></div> <script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.1.0/umd/react.production.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.1.0/umd/react-dom.production.min.js"></script>- Объявляем контекст
Просто у общего родителя объявить состояние и передавать дочерним компонентам. Но так вам придётся по всей цепочке дочерних компонентов пройтись, пока не доберётесь до нужного
const {useState, useContext, createContext} = React; const Item = ({setItems, pushValue}) => { return ( <button onClick={() => setItems((items) => [...items, pushValue])}>{pushValue.name}</button> ) } const Items = ({setItems}) => { return ( <React.Fragment> <Item setItems={setItems} pushValue={{id: 1, name: 'Item 1'}} /> <Item setItems={setItems} pushValue={{id: 2, name: 'Item 2'}} /> <Item setItems={setItems} pushValue={{id: 3, name: 'Item 3'}} /> </React.Fragment> ) } const Modal = ({items}) => { return ( <div className='block'>{items.map(item => item.name)}</div> ) } const App = () => { const [items, setItems] = useState([1]); return ( <React.Fragment> <Items setItems={setItems} /> <Modal items={items} /> </React.Fragment> ) }; ReactDOM.render( <App />, document.getElementById('root') )body { background-color: #777; } .block { margin-top: 8px; }<div id="root"></div> <script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.1.0/umd/react.production.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.1.0/umd/react-dom.production.min.js"></script>