Проверяйте HTTP-статус до res.json(): 404 означает пустой результат поиска. Храните имя и страницу в состоянии, отменяйте старый запрос при их изменении.
Получение списка персонажей через API
Проверяйте HTTP-статус до res.json(): 404 означает пустой результат поиска. Храните имя и страницу в состоянии, отменяйте старый запрос при их изменении.
Короткий ответ
Что ответить на собеседовании
Подробный разбор
Ответ с пояснениями
Условие
Создайте компонент, который будет загружать данные о персонажах из открытого API Rick and Morty.
Формат ответа API
{
"info": {
"count": number,
"pages": number,
"next": string | null, // ссылка на следующую страницу
"prev": string | null // ссылка на предыдущую страницу
},
"results": [
{ "name": string, "id": number }
]
}
Если запрос выполнен с именем, которого нет в базе, сервер возвращает ошибку 404.
Пример функции‑запроса (можно менять имена переменных, но структура кода должна оставаться той же):
function fetchCharacters(query, pageNumber = 1, options = {}) {
return fetch(
`https://rickandmortyapi.com/api/character?name=${query}&page=${pageNumber}`,
options
)
.then(res => res.json())
}
export default function App() {
return '20 minutes adventure'
}
Файл‑пример (json‑ответ от API) приведён ниже и может использоваться для локального тестирования.
{"info":{"count":107,"pages":6,"next":"https://rickandmortyapi.com/api/character?page=2&name=rick","prev":null},"results":[{"id":1,"name":"Rick Sanchez","status":"Alive","species":"Human","type":"","gender":"Male","origin":{"name":"Earth (C-137)","url":"https://rickandmortyapi.com/api/location/1"},"location":{"name":"Citadel of Ricks","url":"https://rickandmortyapi.com/api/location/3"},"image":"https://rickandmortyapi.com/api/character/avatar/1.jpeg","episode":["https://rickandmortyapi.com/api/episode/1","https://rickandmortyapi.com/api/episode/2","https://rickandmortyapi.com/api/episode/3","https://rickandmortyapi.com/api/episode/4","https://rickandmortyapi.com/api/episode/5","https://rickandmortyapi.com/api/episode/6","https://rickandmortyapi.com/api/episode/7","https://rickandmortyapi.com/api/episode/8","https://rickandmortyapi.com/api/episode/9","https://rickandmortyapi.com/api/episode/10","https://rickandmortyapi.com/api/episode/11","https://rickandmortyapi.com/api/episode/12","https://rickandmortyapi.com/api/episode/13","https://rickandmortyapi.com/api/episode/14","https://rickandmortyapi.com/api/episode/15","https://rickandmortyapi.com/api/episode/16","https://rickandmortyapi.com/api/episode/17","https://rickandmortyapi.com/api/episode/18","https://rickandmortyapi.com/api/episode/19","https://rickandmortyapi.com/api/episode/20","https://rickandmortyapi.com/api/episode/21","https://rickandmortyapi.com/api/episode/22","https://rickandmortyapi.com/api/episode/23","https://rickandmortyapi.com/api/episode/24","https://rickandmortyapi.com/api/episode/25","https://rickandmortyapi.com/api/episode/26","https://rickandmortyapi.com/api/episode/27","https://rickandmortyapi.com/api/episode/28","https://rickandmortyapi.com/api/episode/29","https://rickandmortyapi.com/api/episode/30","https://rickandmortyapi.com/api/episode/31","https://rickandmortyapi.com/api/episode/32","https://rickandmortyapi.com/api/episode/33","https://rickandmortyapi.com/api/episode/34","https://rickandmortyapi.com/api/episode/35","https://rickandmortyapi.com/api/episode/36","https://rickandmortyapi.com/api/episode/37","https://rickandmortyapi.com/api/episode/38","https://rickandmortyapi.com/api/episode/39","https://rickandmortyapi.com/api/episode/40","https://rickandmortyapi.com/api/episode/41","https://rickandmortyapi.com/api/episode/42","https://rickandmortyapi.com/api/episode/43","https://rickandmortyapi.com/api/episode/44","https://rickandmortyapi.com/api/episode/45","https://rickandmortyapi.com/api/episode/46","https://rickandmortyapi.com/api/episode/47","https://rickandmortyapi.com/api/episode/48","https://rickandmortyapi.com/api/episode/49","https://rickandmortyapi.com/api/episode/50","https://rickandmortyapi.com/api/episode/51"],"url":"https://rickandmortyapi.com/api/character/1","created":"2017-11-04T18:48:46.250Z"},...]}"```
Ответ
Проверяйте HTTP-статус до res.json(): 404 означает пустой результат поиска. Храните имя и страницу в состоянии, отменяйте старый запрос при их изменении.
Сохраните структуру fetch → then, добавив кодирование имени и проверку статуса:
function fetchCharacters(query, pageNumber = 1, options = {}) {
return fetch(`https://rickandmortyapi.com/api/character?name=${encodeURIComponent(query)}&page=${pageNumber}`, options)
.then(res => {
if (res.status === 404) return { info: { pages: 0 }, results: [] };
if (!res.ok) throw new Error('Ошибка загрузки');
return res.json();
});
}
Компонент с поиском и пагинацией:
import { useEffect, useState } from 'react';
export default function App() {
const [filter, setFilter] = useState({ query: '', page: 1 });
const [data, setData] = useState({ info: { pages: 0 }, results: [] });
const [loading, setLoading] = useState(true);
const [error, setError] = useState('');
useEffect(() => {
const controller = new AbortController();
setLoading(true); setError('');
fetchCharacters(filter.query, filter.page, { signal: controller.signal })
.then(result => { if (!controller.signal.aborted) setData(result); })
.catch(e => { if (!controller.signal.aborted) setError(e.message); })
.finally(() => { if (!controller.signal.aborted) setLoading(false); });
return () => controller.abort();
}, [filter]);
return <section>
<label>Имя <input value={filter.query} onChange={e =>
setFilter({ query: e.target.value, page: 1 })} /></label>
{loading ? <p>Загрузка…</p> : error ? <p role="alert">{error}</p> :
data.results.length ? <ul>{data.results.map(item => <li key={item.id}>{item.name}</li>)}</ul>
: <p>Ничего не найдено</p>}
<button disabled={loading || filter.page === 1} onClick={() =>
setFilter(f => ({ ...f, page: f.page - 1 }))}>Назад</button>
<span> {filter.page} </span>
<button disabled={loading || filter.page >= data.info.pages} onClick={() =>
setFilter(f => ({ ...f, page: f.page + 1 }))}>Вперёд</button>
</section>;
}
Приведённый в условии псевдо-JSON с типами/многоточием нельзя передавать JSON.parse без преобразования в настоящий fixture.