requestAnimationFrame и React-батчинг: почему дёргается drag-and-drop
requestAnimationFrame и React-батчинг: почему дёргается drag-and-drop Сделал перетаскивание элементов в production? На быстрых движениях начинаются дёргания и выпадающие кадры. Проблема знакомая: requestAnimationFrame вызывается до того, как React применяет батч из setState. Особенно если rAF висит на mousemove вне реактовских обработчиков. В React 18+ батчинг работает в рамках одного события, но rAF срабатывает до мерджа обновлений. Результат: анимация читает старую позицию, React применяет новые значения с задержкой, элемент дёргается. Решение с flushSync Для чтения DOM-значений (позиция, размер) используй useLayoutEffect — выполняется до показа кадра. Внутри drag-логики добавь синхронный вызов: const handleMouseMove = (e: MouseEvent) => { ReactDOM.flushSync(() => { setPos((prev) => ({ x: prev.x + e.movementX, y: prev.y + e.movementY })); }); requestAnimationFrame(updateStyles); }; flushSync заставляет React применить батч синхронно перед следующим кадром. Дёргания уходят. Предупреждение Не злоупотребляй flushSync — он ломает Concurrent Mode и режет производительность, если вызывать на каждом mousemove. Типичная ошибка: считать его универсальным решением без оценки trade-offs. Альтернативы - Замени mousemove/touchmove на pointer-events — они синхроннее в браузере. - Если хочешь не трогать React-состояние внутри кадра, храни позиции в useRef и обновляй DOM руками через rAF. Но это редкий кейс, и он разрушает реактивность. Вывод: requestAnimationFrame и React-батчинг конфликтуют по умолчанию — контролируй синхронизацию через flushSync или выноси анимации за пределы реактовского цикла, иначе даже гладкий код споткнётся на быстром перемещении.