Перенести логіку отримання даних з FakeStoreAPI (яку ми робили у ПР №13 на чистому JS) у React-компонент. Опанувати хук useEffect для виконання побічних ефектів (Side Effects) після рендерингу компонента, а також обробку станів завантаження (loading) та помилок (error).
[products, setProducts], [isLoading, setIsLoading], [error, setError].useEffect для ініціалізації завантаження при першому рендері.fetchData всередині useEffect.Увага: Продовжуємо роботу у файлі App.jsx (або ProductsList.jsx, якщо ви винесли його окремо). Видаляємо наш хардкодний масив mockProducts.
useState та useEffect з React.Створіть 3 незалежних стани для контролю над даними:
import { useState, useEffect } from "react";
import { ProductCard } from "./components/ProductCard";
export function App() {
const [products, setProducts] = useState([]); // Початковий стан - порожній масив
const [isLoading, setIsLoading] = useState(true); // Спочатку ми у стані завантаження
const [error, setError] = useState(null); // Поки що помилок немає
// ... Інший код
}
useEffect, який виконається тільки 1 раз під час монтування компонента (порожній масив залежностей []).Не можна робити саму функцію хука асинхронною (useEffect(async () => ...)). Замість цього створіть асинхронну функцію ВСЕРЕДИНІ хука і одразу її викличте.
useEffect(() => {
// Наша синка функція для завантаження
const fetchProducts = async () => {
setIsLoading(true); // Про всяк випадок переконуємося
try {
const response = await fetch(
"https://fakestoreapi.com/products?limit=8",
);
// Кидаємо помилку, якщо щось пішло не так (наприклад, 404 або 500)
if (!response.ok) {
throw new Error(`Помилка сервера: ${response.status}`);
}
const data = await response.json();
// Успіх! Оновлюємо стан масиву
setProducts(data);
} catch (err) {
// Щось впало. Записуємо помилку в стан
setError(err.message);
} finally {
// У будь-якому випадку (і при успіху, і при помилці) — вимикаємо спінер
setIsLoading(false);
}
};
fetchProducts(); // Викликаємо щойно створену функцію
}, []); // <--- ЦЕЙ МАСИВ ОБОВ'ЯЗКОВИЙ (щоб не було нескінченного циклу)
Додайте перевірки перед тим, як рендерити масив:
return (
<div className="app-container">
<main className="page-layout">
<h2>Популярні товари</h2>
{/* 1. Показуємо повідомлення про помилку */}
{error && <p className="error-text">Ой! Код впав: {error}</p>}
{/* 2. Показуємо лоадер */}
{isLoading && <div className="spinner">Завантаження...</div>}
{/* 3. Показуємо сітку з товарами (якщо немає помилки і не завантажується) */}
{!isLoading && !error && (
<div className="products-grid">
{products.map((product) => (
<ProductCard
key={product.id}
title={product.title}
price={product.price}
image={product.image}
/* Доведеться використовувати price через долари, або конвертувати */
/>
))}
</div>
)}
</main>
</div>
);
fetch('https://fakesXstoreapi.com/products') на зламане і переконайтеся, що ви бачите повідомлення з тексту помилки (error && ...), а сайт не ламається в “білий екран смерті”.git add . та git commit -m "Fetch products using useEffect from FakeStoreAPI".main.Новий React-додаток “TechShop” навчився самостійно робити запити до бекенду при запуску, показувати користувачеві зворотний зв’язок (індикатор завантаження) і безпечно відмальовувати реальні дані відразу після їх отримання.
document.getElementById) вважаються ефектами, а розрахунок суми чисел — ні?useEffect(..., []) випадково забути передати другий аргумент (порожній масив)? Нащо він потрібен?async function fetchProducts і викликати її, замість того, щоб зробити сам колбек useEffect асинхронним? Що має повертати useEffect насправді згідно правил React (Cleanup function)?&&). Чому конструкція {isLoading && <p>Завантаження</p>} працює?finally { setIsLoading(false) }, якщо ми в кінці try могли просто написати потрібні команди? Що буде, якщо ми вийдемо через catch без finally?