TGStat
TGStat
Введите текст для поиска
Расширенный поиск каналов
  • Язык сайта
    flag Russian flag English flag Uzbek
  • Вход на сайт
  • Каталог
    Каталог каналов и чатов Региональные подборки Тематические подборки Платные каналы Поиск каналов
    Добавить канал/чат
  • Рейтинги
    Рейтинг каналов Рейтинг чатов Рейтинг публикаций
    Рейтинги брендов и персон
  • Аналитика
  • Поиск по публикациям
  • Мониторинг Telegram
  • Продвижение
    Реклама через Яндекс Бизнес Реклама в каналах через TGStat Agency Реклама на сайте TGStat.ru
ТОП - Тёма о программировани

3 Sep, 16:14

Открыть в Telegram Поделиться Пожаловаться

Здарова, работяги!

Потихоньку продолжаю дописывать посты из заметок. В прошлый раз — элементы и файберы, то есть где React держит работу. Теперь — что он с ней делает, когда прилетает обновление. И почему я больше не сужу по счётчику ререндеров, хотя четыре года назад сам учил их считать.

Обновление идёт в две фазы

Render-фаза: React идёт по файберам, зовёт функции компонентов и сверяет свежие элементы с прошлыми. Весь код из тела компонента крутится здесь, чистый JS. Commit-фаза: разница применяется к DOM, тут же бегут useLayoutEffect. Разницы нет — DOM не трогается.

У браузера свой рендер — раскладка и пиксели, и он нужен, только если коммит реально тронул DOM.

Отсюда тезис: вызов функции копеечный, дорого то, что в её теле.

Пример


function Search({ products }) {
const [query, setQuery] = useState('');

return
setQuery(e.target.value)} />

;
}

function Results({ products, query }) {
console.count('Results render');

const rows = rankProducts(products, query); // тяжёлый расчёт
return ;
}


Вводим текст, счётчик растёт. Легко решить: Results ререндерится слишком часто, срочно в memo.

Но memo здесь даже не поможет: query меняется с каждым символом, а с ним и пропсы Results.

Счётчик отвечает не на тот вопрос

Консоль ответила на один вопрос: сколько раз React вызвал функцию. Сколько занял каждый вызов, дошёл ли результат до DOM, из-за React ли вообще тормозит — этого в ней нет.

Один вызов функции не равен одному изменению экрана. Под в dev счётчик удваивается сам по себе. С транзишенами (`startTransition`, `useDeferredValue`) React может прервать или выбросить начатый проход: лог остался, результата нет. В примере их нет — ввод срочный и рендерится синхронно.

Бывает и наоборот: Results вызвался один раз, но rankProducts занял столько, что ввод стал дёрганым. По счётчику эти случаи не различить.

При этом счётчик не бесполезен: «рендерится ли оно вообще без нужды» он показывает за пять секунд. Молчит про то, сколько это стоит.

Что я измеряю вместо этого

Открываю Performance в Chrome, запись, один символ в поле, стоп после обновления таблицы. Трейс ровно того сценария, который тормозит.

На dev-сборке React 19.2+ там сами появляются React Performance tracks, без расширения. Scheduler показывает, срочное обновление или фоновое, и сколько заняли Render, Commit и эффекты; Components — сколько заняли конкретные компоненты и их эффекты. Рядом — обычный JS, layout и paint браузера.

На React до 19.2 — вкладка Profiler в React DevTools: flamegraph по коммитам плюс настройка «Record why each component rendered».

На моём демо к посту один символ — это «+2» в консоли и около четверти секунды на каждый вызов Results на треке. Первое ни о чём, второе — уже диагноз.

Что делать дальше, трек тоже подсказывает:

— широкий Render в Results, а пропсы реально поменялись: memo не спасёт, лечу сам расчёт;
— React отработал быстро, а дальше тянутся layout и paint: ререндеры ни при чём, смотрю, что коммит сделал с DOM;
— Results дёргается часто, но копейками: оставляю в покое.

Где это ломается

Dev-сборка медленнее боевой: проверки, двойной рендер Strict Mode, инструментация. Трейс тут ищет подозрительное место, а не точные цифры — за ними в профилировочную сборку.

И трейс — про одно взаимодействие. Копеечный рендер на каждый скролл в нём выглядит невинно, а в сумме уже нет. Если поддерево от изменившегося стейта не зависит, его render лучше не запускать вовсе — это про структуру дерева и colocation, следующий пост.

Резюме

— render — чистый JS, DOM меняется в коммите, браузер рисует, только если коммит что-то тронул;
— количество вызовов не показывает задержку: вызов копеечный, дорого то, что внутри;
— сначала записать одно тормозящее взаимодействие и найти дорогой этап, потом оптимизировать;
— счётчик остаётся для вопроса «рендерится ли вообще» — с него начнётся пост про colocation.

1.5k 0 28 1 19
Каталог
Каталог каналов и чатов Подборки каналов Поиск каналов Добавить канал/чат
Рейтинги
Рейтинг каналов Telegram Рейтинг чатов Telegram Рейтинг публикаций Рейтинги брендов и персон
API
API статистики API поиска публикаций API Callback
Наши каналы
@TGStat @TGStat_Chat @telepulse @TGStatAPI
Почитать
Академия TGStat Исследование Telegram 2019 Исследование Telegram 2021 Исследование Telegram 2023
Контакты
Справочный центр Поддержка Почта Вакансии
Всякая всячина
Пользовательское соглашение Политика конфиденциальности Публичная оферта
Наши боты
@TGStat_Bot @SearcheeBot @TGAlertsBot @tg_analytics_bot @TGStatChatBot