Ревью: загрузка по поисковому запросу
Вопрос
Перед тобой код на ревью. Скажи вслух, что здесь не так - по убыванию важности.
Читай вслух, как на ревью: сначала что делает код, потом что сломано.
function Search({ query }) {
const [results, setResults] = useState([]);
const [loading, setLoading] = useState(false);
useEffect(() => {
setLoading(true);
fetch(`/api/search?q=${query}`)
.then((r) => r.json())
.then((data) => {
setResults(data.items);
setLoading(false);
});
}, [query]);
return loading ? <Spinner /> : <List items={results} />;
}
Попробуйте сами
Ответьте вслух за 2 минуты, как на собесе. Потом сверьтесь с тезисами: что прозвучало, а что нет.
Показать ответ и разбор
Коротко
Что должно прозвучать в ответе
гонка запросов: поздний ответ на старый запрос затрёт актуальный
нет отмены и нет флага устаревания в очистке эффекта
нет обработки ошибки - reject оставит loading навсегда
query не экранирован через encodeURIComponent
нет debounce - запрос на каждое нажатие клавиши
обновление состояния после размонтирования
Что могут спросить следом
- Чем отмена лучше флага alive?
- Где бы вы поставили debounce - в компоненте или в хуке?
Разбор
Главное - гонка. Пользователь печатает «рек», уходит три запроса; ответы приходят
в произвольном порядке, и результат по «ре» может прийти последним, затерев правильный.
Лечится флагом в очистке или AbortController:
useEffect(() => {
const ac = new AbortController();
fetch(url, { signal: ac.signal })...
return () => ac.abort();
}, [query]);
Ошибки не обработаны. Любой сбой сети оставляет loading в true навсегда - вечный
спиннер вместо сообщения.
Экранирование. query подставляется в URL как есть: пробел или & сломают запрос,
нужен encodeURIComponent.
Частота. Запрос уходит на каждое нажатие - нужен debounce примерно на 300 мс.
Отдельно: обновление состояния после размонтирования - то же лечится очисткой.
Ответьте сами, а не прочитайте
В тренажёре вопрос попадёт в повторения и вернётся, когда начнёт забываться. Плюс билеты с зачётом: 20 вопросов, не больше 2 ошибок.