Опанувати управління глобальним станом додатку в React без нескінченної передачі пропсів вниз по дереву (Prop Drilling). Створити CartContext, який буде зберігати масив куплених товарів, і надати доступ до нього будь-якому компоненту (наприклад, Header для лічильника та CheckoutPage для підрахунку суми).
CartContext.jsx.createContext() для ініціалізації контексту.CartProvider (компонента-обгортки) зі стейтом [cart, setCart].addToCart, removeFromCart, clearCart).<CartProvider> у main.jsx.Header та ProductCard через хук useContext.Увага: Продовжуємо роботу в проекті “TechShop”. У нас вже є сторінки та роутінг. Але якщо ви додасте товар на сторінці Home, індикатор у Header про це не дізнається (бо вони не мають спільного батьківського useState). Тут нам і допоможе Context API.
src створіть теку context (поруч з pages та components).src/context/CartContext.jsx.import { createContext, useState, useContext } from 'react';export const CartContext = createContext(); // Пустий контекст
Напишіть компонент CartProvider, який буде “постачальником” даних:
export function CartProvider({ children }) {
// Наш глобальний масив кошика (схоже ми робили в ПР №10 на чистому JS)
const [cart, setCart] = useState([]);
// Функція для кнопки "Купити"
const addToCart = (product) => {
setCart((prevCart) => {
// Шукаємо, чи є вже такий товар
const existingItem = prevCart.find((item) => item.id === product.id);
if (existingItem) {
// Збільшуємо кількість
return prevCart.map((item) =>
item.id === product.id
? { ...item, quantity: item.quantity + 1 }
: item,
);
}
// Якщо товару немає, додаємо новий з quantity: 1
return [...prevCart, { ...product, quantity: 1 }];
});
};
return (
// Передаємо стейт cart та функцію addToCart всім "дітям"
<CartContext.Provider value=>
{children}
</CartContext.Provider>
);
}
src/main.jsx.Імпортуйте <CartProvider> та обгорніть ним ваш <App /> (можна просто всередині або зовні <BrowserRouter>).
import { CartProvider } from "./context/CartContext";
createRoot(document.getElementById("root")).render(
<StrictMode>
<BrowserRouter>
{/* Тепер АБСОЛЮТНО всі компоненти мають доступ до кошика! */}
<CartProvider>
<App />
</CartProvider>
</BrowserRouter>
</StrictMode>,
);
ProductCard.jsx.useContext та сам список CartContext.Витягніть функцію addToCart:
import { useContext } from "react";
import { CartContext } from "../context/CartContext";
export function ProductCard({ title, price, image, id }) {
// Переконайтеся, що id теж передається в пропсах!
// Підключаємося до глобального провайдера
const { addToCart } = useContext(CartContext);
// Функція-обгортка для кліку
const handleBuyClick = () => {
// Передаємо ВЕСЬ об'єкт товару
addToCart({ id, title, price, image });
alert(`${title} додано в кошик!`);
};
return (
<article className="product-card">
{/* ... */}
<button className="btn btn-buy" onClick={handleBuyClick}>
Купити
</button>
</article>
);
}
Header.jsx.Аналогічно використайте useContext, але витягніть масив cart:
import { useContext } from "react";
import { CartContext } from "../context/CartContext";
import { Link } from "react-router-dom";
export function Header() {
const { cart } = useContext(CartContext);
// Рахуємо сумарну кількість усіх одиниць товарів (reduce)
const totalItems = cart.reduce((sum, item) => sum + item.quantity, 0);
return (
<header className="header">
<div className="logo">
<Link to="/">TechShop React</Link>
</div>
<div className="user-actions">
<Link to="/checkout" className="btn btn-cart">
Кошик <span>({totalItems})</span>
</Link>
</div>
</header>
);
}
Home миттєво змінюють цифру в кошику у Header. Навіть після переходу на сторінку /checkout цифра зберігається (бо контекст живе в main.jsx, вище за Routes).git add . та git commit -m "Implement Global Cart State using React Context API".main.Проблема “Prop Drilling” ефективно вирішена. Вам більше не потрібно передавати масив кошика через 10 проміжних файлів у React. Дані живуть в ізольованому глобальному сховищі (Контексті) в пам’яті комп’ютера.
children у визначенні провайдера контексту (function CartProvider({ children }))? Куди цей тег монтується і нащо він нам потрібен?value в компоненті <CartContext.Provider value=>. Чи можна передати в нього звичайний масив, рядок, число, об’єкт або функцію?value?handleBuyClick у ProductCard викликає addToCart як звичайну функцію (з круглими дужками), а не просто передається на подію onClick={addToCart}?