Как передавать данные в обработчик сабмита, помещенный в useCallback в React
Что проверяют
Понимаете ли вы замыкания и массив зависимостей: функция в useCallback видит значения того рендера, в котором создана. Следом спрашивают, как сделать обработчик стабильным и не читать устаревшие данные, и нужен ли здесь useCallback вообще.
Как отвечать
Способов передать данные несколько, выбирайте по ситуации:
- через зависимости: значение из состояния указано в массиве,
useCallback(fn, [value]). Корректно, но функция пересоздаётся при каждом вводе, и мемоизация теряет смысл; - из самого события:
new FormData(event.currentTarget)внутри обработчика. Данные берутся из формы в момент сабмита, зависимостей нет, подходит для неконтролируемых полей; - аргументом: дочерняя форма сама вызывает
onSubmit(values), а родитель получает данные параметром. Захватывать нечего, массив зависимостей пустой; - через ref: последнее значение кладут в
useRefи читаютref.currentв стабильном колбэке. Рабочий приём, но менять ref во время рендера нельзя, обновляют его в эффекте или обработчике; - функциональный апдейт: если нужно только обновить состояние,
setState(prev => ...)снимает зависимость от текущего значения.
Затем скажите, зачем всё это. useCallback полезен, когда функция уходит в компонент под memo или в зависимости эффекта. Для обычной <form onSubmit> он ничего не даёт. В React 19 функцию можно передать в <form action> и получить FormData аргументом, а useActionState добавит состояние отправки. React Compiler мемоизирует такие вещи сам, и ручной useCallback в новых проектах нужен реже.
Где спотыкаются
Оставляют пустой массив зависимостей и читают состояние внутри. Обработчик навсегда видит начальное значение, и форма отправляет пустую строку.
Отключают правило react-hooks/exhaustive-deps, чтобы линтер не мешал. Это прячет ту же ошибку устаревшего замыкания.
Заворачивают в useCallback каждый обработчик, хотя потребитель не обёрнут в memo. Кода больше, выгоды нет.