Запускайте запрос в эффекте по значению input, пропускайте пустую строку и игнорируйте ответ отменённого эффекта. Отдельно храните loading, результат и ошибку.
Управление вводом пользователя с синхронизацией асинхронных запросов
Запускайте запрос в эффекте по значению input, пропускайте пустую строку и игнорируйте ответ отменённого эффекта. Отдельно храните loading, результат и ошибку.
Короткий ответ
Что ответить на собеседовании
Подробный разбор
Ответ с пояснениями
Условие
Создать небольшое React‑приложение, которое:
- Содержит поле ввода текста;
- При каждом изменении значения инициирует запрос к серверу;
- Выводит полученный от сервера ответ под полем ввода.
Особые условия реализации:
- Показать индикатор, пока запрос находится в процессе выполнения;
- Не выполнять запрос, если поле ввода пустое;
- Обеспечить корректную работу при получении ответов в произвольном порядке – отображать только результат последнего отправленного запроса.
import React from 'react';
export const App = () => {
return <div/>;
};
Ответ
Запускайте запрос в эффекте по значению input, пропускайте пустую строку и игнорируйте ответ отменённого эффекта. Отдельно храните loading, результат и ошибку.
Backend-контракт не указан, поэтому компонент получает функцию request(query, { signal }), возвращающую Promise.
export function App({ request }) {
const [value, setValue] = React.useState('');
const [result, setResult] = React.useState('');
const [loading, setLoading] = React.useState(false);
const [error, setError] = React.useState('');
React.useEffect(() => {
const controller = new AbortController();
setResult(''); setError('');
if (!value.trim()) { setLoading(false); return; }
setLoading(true);
Promise.resolve().then(() => request(value, { signal: controller.signal }))
.then(text => { if (!controller.signal.aborted) setResult(text); })
.catch(e => { if (!controller.signal.aborted) setError(e.message || 'Ошибка запроса'); })
.finally(() => { if (!controller.signal.aborted) setLoading(false); });
return () => controller.abort();
}, [value, request]);
return <div>
<label>Запрос <input value={value} onChange={e => setValue(e.target.value)} /></label>
{loading ? <p role="status">Загрузка…</p> : error ? <p role="alert">{error}</p> : <p>{result}</p>}
</div>;
}
Ссылка request должна быть стабильной. Даже если транспорт не умеет отменяться, проверка signal не даст устаревшему ответу перезаписать новое состояние. Debounce здесь не добавлен, поскольку условие требует запрос при каждом изменении.