Debounce и throttle

В библиотеке Atomico управление производительностью компонентов при работе с событиями является ключевым аспектом. События, такие как ввод пользователя, скролл или ресайз окна, могут вызывать многочисленные ререндеры и вычисления, если их обрабатывать напрямую. Для решения этой задачи используются техники debounce и throttle, которые помогают контролировать частоту вызова функций.


Debounce

Debounce позволяет откладывать выполнение функции до тех пор, пока не прекратится поток событий. Это особенно полезно при работе с вводом текста, где функция обновления состояния должна срабатывать только после того, как пользователь закончил ввод.

Пример использования debounce в Atomico:

import { component, useState, useEffect } FROM "atomico";

function debounce(fn, delay) {
    let timer;
    return (...args) => {
        clearTimeout(timer);
        timer = setTimeout(() => fn(...args), delay);
    };
}

function SearchInput() {
    const [query, setQuery] = useState("");

    const handleInput = debounce((event) => {
        setQuery(event.target.value);
    }, 300);

    return (
        <input type="text" onIn put={handleInput} placeholder="Поиск..." />
    );
}

export default component(SearchInput);

Ключевые моменты:

  • clearTimeout отменяет предыдущий вызов, если событие произошло снова до истечения таймера.
  • setTimeout откладывает выполнение функции на указанное время.
  • debounce уменьшает количество обновлений состояния и ререндеров, что повышает производительность.

Throttle

Throttle гарантирует, что функция будет вызываться не чаще одного раза за заданный интервал времени. Это важно для событий с высокой частотой, например, при скролле страницы или ресайзе окна.

Пример использования throttle в Atomico:

import { component, useState, useEffect } FROM "atomico";

function throttle(fn, LIMIT) {
    let lastCall = 0;
    return (...args) => {
        const now = Date.now();
        if (now - lastCall >= LIMIT) {
            lastCall = now;
            fn(...args);
        }
    };
}

function ScrollTracker() {
    const [scroll, setScroll] = useState(0);

    useEffect(() => {
        const handleScroll = throttle(() => {
            setScroll(window.scrollY);
        }, 200);

        window.addEventListener("scroll", handleScroll);
        return () => window.removeEventListener("scroll", handleScroll);
    }, []);

    return <div>Позиция скролла: {scroll}px</div>;
}

export default component(ScrollTracker);

Особенности throttle:

  • Функция вызывается с интервалом, независимо от частоты событий.
  • Позволяет контролировать нагрузку на ререндеры и тяжелые вычисления.
  • Подходит для постоянных потоков событий, где важно фиксированное обновление состояния.

Сравнение Debounce и Throttle

Характеристика Debounce Throttle
Выполнение функции После прекращения потока событий С интервалом в течение потока событий
Применение Ввод текста, фильтры, автокомплит Скролл, ресайз, анимации
Частота вызова Один раз после паузы Ограничена заданным интервалом

Выводы для Atomico:

  • Debounce оптимизирует обновления состояния, предотвращая множественные ререндеры при непрерывном вводе.
  • Throttle обеспечивает стабильное и предсказуемое обновление данных при событиях высокой частоты.
  • Использование этих техник совместно с хуками useEffect и useState позволяет создавать высокопроизводительные компоненты.

Практическое применение

В компонентах Atomico часто встречается сочетание событийного ввода и асинхронных запросов. Например, при создании поисковой панели с автодополнением:

import { component, useState, useEffect } from "atomico";

function SearchAutocomplete() {
    const [query, setQuery] = useState("");
    const [results, setResults] = useState([]);

    const fetchResults = debounce(async (q) => {
        const response = await fetch(`/api/search?q=${q}`);
        const data = await response.json();
        setResults(data);
    }, 300);

    useEffect(() => {
        if (query) fetchResults(query);
    }, [query]);

    return (
        <div>
            <input onIn put={e => setQuery(e.target.value)} />
            <ul>
                {results.map(item => <li key={item.id}>{item.name}</li>)}
            </ul>
        </div>
    );
}

export default component(SearchAutocomplete);

Такой подход обеспечивает:

  • Отложенный вызов запросов до завершения ввода.
  • Снижение нагрузки на сервер и ререндер компонента.
  • Плавный пользовательский опыт без «подергиваний».

Debounce и throttle являются базовыми инструментами оптимизации производительности в Atomico, позволяя управлять частотой вызова функций и предотвращать излишние ререндеры при интенсивных событиях. Они легко интегрируются с хуками и компонентной архитектурой, обеспечивая чистый и предсказуемый код.