> Нужно ли использовать useEffect для подписки на изменения неконтролируемого input с ref в React? (React)

Уровень: senior · Роль: frontend · Категория: Технические вопросы

Компании: Альфа-банк

Стек: React

> Пример ответа

Короткий ответ

Нет, использовать useEffect для подписки на изменения неконтролируемого input с ref не нужно и неэффективно. useEffect срабатывает асинхронно после рендера, что создаёт задержку и лишние ререндеры. Вместо этого следует использовать нативный DOM-обработчик через ref.current.addEventListener или, если требуется реактивность, перейти на контролируемый компонент с useState.

Подробное объяснение

useEffect предназначен для синхронизации с внешними системами (API, таймеры, подписки), а не для отслеживания изменений DOM-элементов, управляемых пользователем. При использовании useEffect для чтения ref.current.value возникает несколько проблем:

  • Задержка обновления: эффект выполняется после рендера, поэтому значение input будет устаревшим на момент чтения.
  • Лишние ререндеры: изменение состояния внутри эффекта вызывает повторный рендер, что может привести к бесконечному циклу или лишним вычислениям.
  • Нарушение реактивности: React не отслеживает изменения DOM напрямую, поэтому эффект не сработает при каждом вводе пользователя - только при ререндере компонента.

Правильный подход - использовать нативный onChange (если нужна реакция на каждое изменение) или onBlur (если нужно значение только при потере фокуса). Для неконтролируемого input ref нужен только для чтения значения по событию, а не для подписки.

На практике

В реальных проектах неконтролируемые input с ref используются в формах с большим количеством полей для избежания ререндеров при каждом вводе. Типичный сценарий - форма с кнопкой "Отправить", где значение читается в обработчике submit:

JSX
function Form() {
const inputRef = useRef(null);
const handleSubmit = (e) => {
e.preventDefault();
console.log(inputRef.current.value); // читаем значение только при submit
};
return (
<form onSubmit={handleSubmit}>
<input ref={inputRef} defaultValue="" />
<button type="submit">Отправить</button>
</form>
);
}

Если же требуется реагировать на каждое изменение (например, валидация в реальном времени), лучше использовать контролируемый компонент с useState и onChange.

Пример кода

Неправильный подход с useEffect:

JSX
function BadInput() {
const ref = useRef(null);
const [value, setValue] = useState('');
useEffect(() => {
// ❌ не сработает при вводе пользователя
setValue(ref.current.value);
}, []);
return <input ref={ref} />;
}

Правильный подход с событием:

JSX
function GoodInput() {
const ref = useRef(null);
const [value, setValue] = useState('');
const handleChange = () => {
setValue(ref.current.value);
};
return <input ref={ref} onChange={handleChange} />;
}

Как отвечать на собеседовании

Начни с прямого отрицательного ответа, затем объясни причину: useEffect не предназначен для подписки на DOM-события. Упомяни альтернативы - нативные обработчики или контролируемые компоненты. Покажи понимание разницы между контролируемыми и неконтролируемыми компонентами. Если спросят про производительность, отметь, что useEffect добавляет лишний рендер, а нативный onChange работает синхронно.

Что проверяет интервьюер

  • Понимание жизненного цикла React и работы useEffect.
  • Знание разницы между контролируемыми и неконтролируемыми компонентами.
  • Умение выбирать правильный инструмент для задачи, а не использовать useEffect везде.
  • Понимание производительности и ререндеров.
  • Знание нативных DOM-событий и их интеграции с React.

Типичные ошибки

  • Использование useEffect с пустым массивом зависимостей для подписки на изменения input - эффект выполняется только один раз и не отслеживает последующие изменения.
  • Попытка синхронизировать ref и state через useEffect с зависимостью от ref.current - это не сработает, так как ref.current не является реактивной зависимостью.
  • Добавление ref.current.value в зависимости useEffect - это не вызовет повторный эффект при изменении значения input.
  • Использование useEffect для установки обработчика через addEventListener - это избыточно, если можно использовать встроенный onChange.

> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?

Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью