Запрашивайте подсказки с небольшой задержкой после ввода, отменяйте предыдущий запрос и показывайте результаты через доступный список вариантов.
Создание автодополняющего Input‑компонента в React
Запрашивайте подсказки с небольшой задержкой после ввода, отменяйте предыдущий запрос и показывайте результаты через доступный список вариантов.
Короткий ответ
Что ответить на собеседовании
Подробный разбор
Ответ с пояснениями
Условие
В задании предоставлен API, возвращающий перечень персонажей из вселенной «Звёздных войн». Требуется написать React‑компонент InputAutocomplete, который будет предлагать варианты имен персонажей по мере ввода пользователем первых букв. При вводе часть строки, компонент должен показывать список подходящих подсказок, полученных через указанный API.
Описание API:
GET https://swapi.dev/api/people?search=skywalker&page=1
{
next: string | null // url
results: Array({ name: string, url: string }) // max length 10, unique url
}
Исходный код проекта:
import React from 'react';
function getPeople(search, page = 1, options = {}) {
return fetch(
`https://swapi.dev/api/people?search=${search}&page=${page}`,
options
)
.then((res) => res.json())
.then((data) => data);
}
export default function App() {
return 'Hello there!';
}
Ваша задача – реализовать компонент автодополнения, используя функцию getPeople для получения данных, и интегрировать его в приложение.
Ответ
Запрашивайте подсказки с небольшой задержкой после ввода, отменяйте предыдущий запрос и показывайте результаты через доступный список вариантов.
В getPeople добавьте кодирование параметров и проверку HTTP-ошибки:
async function getPeople(search, page = 1, options = {}) {
const params = new URLSearchParams({ search, page: String(page) });
const response = await fetch('https://swapi.dev/api/people?' + params, options);
if (!response.ok) throw new Error('HTTP ' + response.status);
return response.json();
}
Простой вариант с нативным datalist:
function InputAutocomplete() {
const [query, setQuery] = React.useState('');
const [items, setItems] = React.useState([]);
const [status, setStatus] = React.useState('');
const id = React.useId();
React.useEffect(() => {
const controller = new AbortController();
setItems([]);
if (!query.trim()) { setStatus(''); return; }
setStatus('Загрузка…');
const timer = setTimeout(async () => {
try {
const data = await getPeople(query.trim(), 1, { signal: controller.signal });
if (!Array.isArray(data.results)) throw new Error('Формат');
if (!controller.signal.aborted) { setItems(data.results); setStatus(data.results.length ? '' : 'Нет совпадений'); }
} catch { if (!controller.signal.aborted) setStatus('Ошибка загрузки'); }
}, 300);
return () => { clearTimeout(timer); controller.abort(); };
}, [query]);
return <label>Персонаж
<input list={id} value={query} onChange={e => setQuery(e.target.value)} />
<datalist id={id}>{items.map(item => <option key={item.url} value={item.name} />)}</datalist>
<span role='status'>{status}</span>
</label>;
}
// В App: return <InputAutocomplete />;
Показаны первые 10 результатов по контракту API. Для полного списка добавляют загрузку следующей страницы по next; debounce не заменяет защиту от устаревшего ответа.