Оптимизация React js приложений. Использование функции debounde()
-
Допустим мы имеем очень абстрактный компонент, который имеет поле ввода текста, кнопку отправки этого текста на сервер, и сообщение о состоянии отправки данных на сервер
Пример кода
// функция иммитации запроса на сервер function fakeFetch(val: string) { return new Promise<string>(function (resolve) { setTimeout(function () { resolve(`Сервер получил сообщение: ${val}`); }, 1000); }); } // самый простой пример компонента function App() { const [val, setVal] = useState(''); const [response, setResponse] = useState(''); const inputHandler = (e: React.ChangeEvent<HTMLInputElement>) => { setVal(e.target.value); setResponse(''); }; const submitHandler = async () => { setVal(''); setResponse('отправка ...'); const resp = await fakeFetch(val); setResponse(resp); }; return ( <> <input type="text" value={val} onInput={inputHandler} /> <button onClick={submitHandler}>Отправить</button> <p>{response}</p> </> ); }
Теперь перед нами встала задача заменить поведение: отправлять данные не по кнопке, а автоматически. Однако мы не может просто так повесить функцию в обработчик inputHandler(), мало того что он будет неоправданно часто отправлять запросы на сервер, так еще и будет сбивать значение
val
. Но если мы добавить функциюdebounce()
, то она решит эти вопросы.Функция debounce()
debounce() - это функция-обертка, которая ограничивает число выполнений переданной в нее функции, некоторым промежутком времени. Если точнее, то, пока промежуток времени не прошел, то зарегистрированная функция не будет исполнятся, а пока она не исполнилась, новый вызов функции будет замещать старый, исключая повторное выполнение.
пример:
function debounce(func, delay: number) { let timeoutId: number; return (...args) => { clearTimeout(timeoutId); timeoutId = setTimeout(() => func(...args), delay); }; }
Как правильно использовать в React компоненте
function App() { const [val, setVal] = useState(''); const [response, setResponse] = useState(''); const inputHandler = (e: React.ChangeEvent<HTMLInputElement>) => { setVal(e.target.value); setResponse(''); debounceHandler(); }; const submitHandler = useCallback(async () => { setResponse('отправка ...'); const resp = await fakeFetch(val); setVal(''); setResponse(resp); }, []); const debounceHandler = useMemo(() => { return debounce(submitHandler, 800); }, [submitHandler]); return ( <> <input type="text" value={val} onInput={inputHandler} /> {/* <button onClick={submitHandler}>Отправить</button> */} <p>{response}</p> </> ); }
При изменении стейта функциональный компонент вызывает свою функцию каждый раз, и без мемоизации мы бы получали каждый раз новый обработчик
debounceHandler()
иsubmitHandler()
. Но для того чтобы все работало нам необходимо передать одну функцию в debounce один раз!Поэтому мемоизируем submitHandler() через
useCallback
(линтер обычно потребует указатьsubmitHandler
как зависимостьdebounceHandler
) и саму функциюdebounceHandler()
вuseMemo
, (т.к debounce() возвращает функцию) -
Простыми словами, мы колбэчим submitHandler через debounceHandler то как оно должно быть по спецификации JS ? И почему onInput, а не onChange ?
-
Пользователь @kirilljs написал в Оптимизация React js приложений. Использование функции debounde():
Простыми словами, мы колбэчим submitHandler через debounceHandler то как оно должно быть по спецификации JS
не понял вопроса, напомню, чтчо тут не используется никаких форм, фактически все обработчики привязаны к onInput событию, функция
debounce()
- самописная -
Пользователь @kirilljs написал в Оптимизация React js приложений. Использование функции debounde():
И почему onInput, а не onChange
onInput отлавливает все изменения значения в
<input />
, даже такие как нажатие клавиш и вставку текста, поэтому он предпочтительнее в данном случае -
@kirilljs Возможно магия реакта onInput и onChange делает идентичными, https://github.com/facebook/react/issues/3964
Скорее всего можно использовать и то и то