Как фронтенд-разработчики, работающие с React, мы постоянно используем хук useEffect
для рендеринга побочных эффектов.
Но знаете ли вы, что есть похожий, но немного другой хук под названием useLayoutEffect
? Я этого не делал, пока не наткнулся на пример этого, использованный в одной из кодовых баз, в которых я участвовал.
Это блог, чтобы понять суть различий между ними и понять, когда и когда не использовать каждый из них.
Разница
В двух словах разница заключается в точности.
useEffect
запускается, но нет никакой гарантии, когда именно это произойдет. Это асинхронная операция, которая выполняется после компоновки и рисования пикселей.useLayoutEffect
, напротив, гарантированно запускается немедленно. Это синхронная операция, которая происходит после всех изменений DOM и до того, как браузер сможет отрисовать пиксели. Если вы знакомы с жизненным цикломcomponentDidMount
в компонентах класса React, это в основном эквивалентное поведение.
useLayoutEffect кажется лучше. Должны ли мы использовать его для всего?
Но почему, при прочих равных, мы не хотим использовать useLayoutEffect
все время?
Не так быстро. Как и все остальное, точность имеет свою цену.
React оптимизировал useEffect
, чтобы сделать его более производительным. Нам нужно много программировать побочные эффекты при использовании React, и улучшения производительности, встроенные в useEffect
, существенно сложатся.
Команда React настоятельно рекомендует пользователям использовать useEffect
как можно чаще, чтобы избежать блокировки визуальных обновлений.
По словам официальных документов React в разделе Время эффектов:
… функция, переданная
useEffect
, срабатывает после компоновки и рисования во время отложенного события. Это делает его подходящим для многих распространенных побочных эффектов, таких как настройка подписок и обработчиков событий, потому что большинство типов работы не должны блокировать обновление экрана браузером.
Примечание: команда React недавно обновила useEffect
в React 18, чтобы сделать его синхронным для общих событий, таких как клики пользователя, и еще меньше причин для использования хука useLayoutEffect
.
Когда мы должны использовать useLayoutEffect
?
Итак, когда мы должны подумать об использовании этой мощной, но ущербной силы?
Обычно это сводится к одной основной причине: когда вам нужно измерить DOM и обеспечить немедленную визуальную обратную связь расчета (отсюда и «Макет» в названии). Это важно здесь, поскольку побочные эффекты не могут быть отложены.
- Документация React дает нам пример, в котором мутация DOM вычисляется перед следующей отрисовкой, чтобы гарантировать, что пользователь не заметит визуальные несоответствия.
- Это может включать в себя конкретные варианты использования, такие как анимация или перемещение элементов в DOM, что требует мгновенных вычислений и обратной связи. Использование
useEffect
для этих вариантов использования, вероятно, приведет к нестабильной и непоследовательной работе.
Существует дополнительный вариант использования:
- Как я упоминал выше,
useLayoutEffect
является эквивалентом старого жизненного циклаcomponentDidMount
в компонентах класса React. Этот хук можно использовать, чтобы помочь вам перейти от компонентов класса к компонентам функций, чтобы получить замену один к одному. Однако это должно быть временным решением.
Выводы
Таким образом, в основном useEffect
похож на вашу надежную Toyota Corolla: надежный, производительный и дешевый - идеально подходит для 99,9% случаев использования. useLayoutEffect
, напротив, похож на высококлассный Ferrari: быстрый, отзывчивый, но дорогой, зарезервированный для 0,1%, когда скорость действительно имеет значение.
useEffect
иuseLayoutEffect
используются для побочных эффектов, но последний позволяет более точно контролировать его работу.- Используйте
useEffect
как можно чаще, чтобы воспользоваться преимуществами оптимизации производительности команд React. - Существует очень мало реальных вариантов использования
useLayoutEffect
. Используйте только в том случае, если (1) вам нужно немедленно визуально показать побочные эффекты вашим пользователям или (2) когда вы временно переходите сcomponentDidMount
.