Ревью: debounce в компоненте
Вопрос
Перед тобой код на ревью. Скажи вслух, что здесь не так - по убыванию важности.
Читай вслух, как на ревью: сначала что делает код, потом что сломано.
function Search() {
const [value, setValue] = useState('');
const search = debounce((q) => {
fetch('/api/search?q=' + q).then(/* ... */);
}, 300);
return <input value={value} onChange={(e) => { setValue(e.target.value); search(e.target.value); }} />;
}
Попробуйте сами
Ответьте вслух за 2 минуты, как на собесе. Потом сверьтесь с тезисами: что прозвучало, а что нет.
Показать ответ и разбор
Коротко
Что должно прозвучать в ответе
debounce создаётся заново на каждом рендере - откладывание не работает
нужен useMemo или useRef, чтобы функция пережила рендер
нет отмены отложенного вызова при размонтировании
q не экранирован через encodeURIComponent
нет отмены предыдущего запроса - остаётся гонка
проще перенести задержку в эффект с зависимостью от value
Что могут спросить следом
- Почему вариант с useEffect чище?
- Debounce убирает гонку запросов полностью?
Разбор
Главная ошибка - debounce вызывается в теле компонента. Каждый ввод символа
вызывает рендер, рендер создаёт новую отложенную функцию с новым таймером, и старый таймер
никого не отменяет. В результате запрос уходит на каждое нажатие с задержкой 300 мс -
то есть debounce не работает вообще, хотя код выглядит правильным.
Лечение: useMemo(() => debounce(fn, 300), []) или useRef. Но чище убрать библиотеку
и сделать эффектом:
useEffect(() => {
const id = setTimeout(() => load(value), 300);
return () => clearTimeout(id);
}, [value]);
Здесь очистка сама отменяет предыдущий таймер, и размонтирование обработано.
Остаются два пункта: encodeURIComponent для запроса и отмена самого запроса -
задержка уменьшает число гонок, но не устраняет их.
Ответьте сами, а не прочитайте
В тренажёре вопрос попадёт в повторения и вернётся, когда начнёт забываться. Плюс билеты с зачётом: 20 вопросов, не больше 2 ошибок.