Загрузка данных из API
Большинство приложений получают данные с сервера через API — адрес, который возвращает JSON. В React запрос отправляют в эффекте, а ответ кладут в состояние. Пока ответа нет, показывают «Загрузка…», а если запрос не удался — сообщение об ошибке. С этого урока начинается второй пример курса — небольшой блог.
Новый проект
Создайте проект блога рядом со списком задач:
npm create vite@latest blog -- --template react
cd blog
npm install
npm run devСтатьи будем брать из JSONPlaceholder — бесплатного учебного API. Адрес https://jsonplaceholder.typicode.com/posts возвращает массив из 100 статей с полями id, title и body.
Простой запрос
Создайте src/components/PostList.jsx:
// src/components/PostList.jsx
import { useEffect, useState } from 'react';
export default function PostList() {
const [posts, setPosts] = useState([]);
useEffect(() => {
fetch('https://jsonplaceholder.typicode.com/posts')
.then((response) => response.json())
.then((data) => setPosts(data));
}, []);
return (
<ul>
{posts.map((post) => (
<li key={post.id}>{post.title}</li>
))}
</ul>
);
}Выведите <PostList /> в src/App.jsx. Через мгновение на странице появится список из 100 заголовков. Пустой массив зависимостей означает, что запрос отправится один раз.
Загрузка и ошибка
Перепишем компонент с async/await и тремя состояниями:
// src/components/PostList.jsx
import { useEffect, useState } from 'react';
export default function PostList() {
const [posts, setPosts] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
async function load() {
try {
const response = await fetch('https://jsonplaceholder.typicode.com/posts');
if (!response.ok) {
throw new Error(`Ошибка ${response.status}`);
}
setPosts(await response.json());
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
}
load();
}, []);
if (loading) return <p>Загрузка…</p>;
if (error) return <p>Не удалось загрузить статьи: {error}</p>;
return (
<ul>
{posts.map((post) => (
<li key={post.id}>{post.title}</li>
))}
</ul>
);
}Сначала на странице «Загрузка…», затем список. Измените адрес на несуществующий, например /postsx, — появится «Не удалось загрузить статьи: Ошибка 404». fetch не бросает исключение при кодах 404 и 500, поэтому response.ok проверяют вручную.
Запрос, зависящий от пропса
Если адрес зависит от пропса, его добавляют в зависимости, а в очистке отмечают, что старый ответ больше не нужен:
// src/components/PostComments.jsx
useEffect(() => {
let ignore = false;
fetch(`https://jsonplaceholder.typicode.com/posts/${postId}/comments`)
.then((response) => response.json())
.then((data) => {
if (!ignore) setComments(data);
});
return () => {
ignore = true;
};
}, [postId]);Если postId сменится раньше, чем придёт ответ, старый ответ будет проигнорирован и не перезапишет новые комментарии.
Частая ошибка
Функцию эффекта нельзя объявлять async: она должна вернуть функцию очистки или ничего, а async-функция всегда возвращает промис.
// Неправильно:
useEffect(async () => {
const response = await fetch(url);
}, []);Объявите async-функцию внутри эффекта и вызовите её, как в примере выше. В реальных проектах загрузку часто доверяют библиотеке TanStack Query — она описана в последней главе.