Какая последовательность рендеров и эффектов в React при монтировании и обновлении компонента?
Что проверяют
Понимаете ли вы разницу между фазой рендера и фазой коммита и когда именно запускаются эффекты относительно отрисовки. Следом спрашивают, в каком порядке срабатывают эффекты родителя и ребёнка и почему в разработке эффект вызывается дважды.
Как отвечать
Для функциональных компонентов порядок такой:
- рендер: React вызывает функции компонентов сверху вниз, от родителя к детям. Это чистое вычисление JSX, DOM ещё не тронут, и React вправе повторить или отбросить результат;
- коммит: изменения применяются к DOM, выставляются
ref; useLayoutEffect: синхронно после изменения DOM, до отрисовки кадра. Здесь измеряют размеры, аsetStateотсюда перерисует без мигания;- отрисовка браузером;
useEffect: обычно после отрисовки, не блокируя кадр. Запросы, подписки, аналитика.
Эффекты идут снизу вверх: сначала у детей, потом у родителя, чтобы родитель видел уже смонтированных детей.
При обновлении снова рендер и коммит, затем для эффектов с изменившимися зависимостями сначала выполняется очистка прошлого запуска со старыми значениями, потом новый эффект. Очистка вызывается не только при размонтировании, а перед каждым повторным запуском. Эффект без массива зависимостей срабатывает после каждого рендера, с пустым массивом один раз после монтирования.
У классов монтирование идёт так: constructor, getDerivedStateFromProps, render, componentDidMount. Обновление: getDerivedStateFromProps, shouldComponentUpdate, render, getSnapshotBeforeUpdate, componentDidUpdate. По времени componentDidMount соответствует useLayoutEffect, а не useEffect.
В разработке под StrictMode React монтирует компонент, запускает эффекты, выполняет очистку и запускает их снова. Так он проверяет, что очистка написана правильно. В продакшене этого нет.
Где спотыкаются
Считают, что useEffect срабатывает до отрисовки, и измеряют в нём DOM. Получается мигание, здесь нужен useLayoutEffect.
Уверены, что эффекты родителя идут раньше детских, по аналогии с рендером.
Принимают двойной вызов эффекта за баг и убирают StrictMode, вместо того чтобы дописать очистку.