React useTransition: решает все проблемы с производительностью или нет?
Статья (англ.): https://www.developerway.com/posts/use-transition
В React 18 появился "конкурентный рендеринг", цель которого решить проблемы с производительностью веб-приложений.
‼️Суть проблемы
Обновление стейта в React - штука синхронная. Как только стейт начал обновляться, поток выполнения кода блокируется. Пока не будут произведены все необходимые вычисления, пока не перерендерятся все изменившиеся компоненты, поток будет занят. Если в это время пользователь захочет повзаимодействовать со страницей, у него ничего не получится.
Например, мы собираемся вывести на страницу большой список из сотни пунктов. Каждый пункт - это немаленький компонент, который требует времени на отрисовку. Мы закидываем этот список в стейт - и обновление понеслось. Пока не отрендерятся все 100 пунктов, страница будет "висеть", пользователь не сможет никуда кликнуть, ничего прокрутить.
🔆 Переходные изменения для решения проблемы
React 18 вводит понятие "переходных изменений" - transitions. Переходное изменение в данном случае - это НЕ критичное изменение стейта. Например, вот этот самый список: отрисовать его не так критично, как сохранить интерактивность. Он и так большой, ничего страшного, если придется подождать чуть больше до его появления на странице.
Мы создаем transition и уже внутри него вносим изменения в стейт (закидываем список). React понимает, что это изменения и все изменения, вызванные им, не суперважные. Он начинает их обрабатывать, но если в это время пользователь начнет двигать мышкой, то React все бросит и переключится на пользователя.
Таким образом, "переходы" обрабатываются как бы "в фоне", хотя это не очень правильный термин, ведь JavaScript по-прежнему остается однопоточным языком. Правильнее будет сказать, что обработка переходного изменения просто прерывается, если появляются более срочные дела, а потом React снова к ней возвращается.
Выглядит это все вот так:
‼️Проблема переходных изменений
Проблема скрывается вот тут:
При запуске нового перехода флаг
В статье эта проблема очень хорошо разобрана на примере.
Берем простую страницу с тремя табами, у одного из которых очень тяжелый контент. Смену таба делаем переходным изменением, чтобы пока контент рендерится, пользователь не страдал. Это прекрасно решает проблему открытия тяжелого таба, но создает новую - при открытии легкого таба после тяжелого.
- мы хотим открыть тяжелый таб, запускаем переход
- пользователь ждет (показываем его лоадер), но в это время он может взаимодействовать со страницей (например, нажать другой таб)
- когда переход обработан, отображается тяжелый таб
- теперь пользователь кликает на легкий таб, казалось бы он должен открыться сразу же, но нет, интерфейс виснет
Виснет он именно потому, что меняется флаг
Решение у проблемы есть - это мемоизация. Но в целом следует хорошенько думать, прежде чем использовать конкурентный рендеринг. Это очень тонкий механизм, который требует ясного понимания подкапотной работы React.
#ссылки #concurrent #важно
Статья (англ.): https://www.developerway.com/posts/use-transition
В React 18 появился "конкурентный рендеринг", цель которого решить проблемы с производительностью веб-приложений.
‼️Суть проблемы
Обновление стейта в React - штука синхронная. Как только стейт начал обновляться, поток выполнения кода блокируется. Пока не будут произведены все необходимые вычисления, пока не перерендерятся все изменившиеся компоненты, поток будет занят. Если в это время пользователь захочет повзаимодействовать со страницей, у него ничего не получится.
Например, мы собираемся вывести на страницу большой список из сотни пунктов. Каждый пункт - это немаленький компонент, который требует времени на отрисовку. Мы закидываем этот список в стейт - и обновление понеслось. Пока не отрендерятся все 100 пунктов, страница будет "висеть", пользователь не сможет никуда кликнуть, ничего прокрутить.
🔆 Переходные изменения для решения проблемы
React 18 вводит понятие "переходных изменений" - transitions. Переходное изменение в данном случае - это НЕ критичное изменение стейта. Например, вот этот самый список: отрисовать его не так критично, как сохранить интерактивность. Он и так большой, ничего страшного, если придется подождать чуть больше до его появления на странице.
Мы создаем transition и уже внутри него вносим изменения в стейт (закидываем список). React понимает, что это изменения и все изменения, вызванные им, не суперважные. Он начинает их обрабатывать, но если в это время пользователь начнет двигать мышкой, то React все бросит и переключится на пользователя.
Таким образом, "переходы" обрабатываются как бы "в фоне", хотя это не очень правильный термин, ведь JavaScript по-прежнему остается однопоточным языком. Правильнее будет сказать, что обработка переходного изменения просто прерывается, если появляются более срочные дела, а потом React снова к ней возвращается.
Выглядит это все вот так:
const [list, setList] = useState([])
const [isPending, startTransition] = useTransition()
const showList = () => {
startTransition(() => {
setList(hugeList)
})
}
‼️Проблема переходных изменений
Проблема скрывается вот тут:
const [isPending, startTransition] = useTransition()
При запуске нового перехода флаг
isPending
изменяется, что вызывает дополнительный перерендер компонента. Если компонент сам по себе тяжелый, то этот перерендер сам заблокирует интерактивность страницы.В статье эта проблема очень хорошо разобрана на примере.
Берем простую страницу с тремя табами, у одного из которых очень тяжелый контент. Смену таба делаем переходным изменением, чтобы пока контент рендерится, пользователь не страдал. Это прекрасно решает проблему открытия тяжелого таба, но создает новую - при открытии легкого таба после тяжелого.
- мы хотим открыть тяжелый таб, запускаем переход
- пользователь ждет (показываем его лоадер), но в это время он может взаимодействовать со страницей (например, нажать другой таб)
- когда переход обработан, отображается тяжелый таб
- теперь пользователь кликает на легкий таб, казалось бы он должен открыться сразу же, но нет, интерфейс виснет
Виснет он именно потому, что меняется флаг
isPending
и текущий компонент перерендеривается, а текущий у нас сейчас - тяжелый таб. То есть прежде чем перейти на легкий таб, мы перерендериваем тяжелый.Решение у проблемы есть - это мемоизация. Но в целом следует хорошенько думать, прежде чем использовать конкурентный рендеринг. Это очень тонкий механизм, который требует ясного понимания подкапотной работы React.
#ссылки #concurrent #важно
Developerway
React useTransition: performance game changer or...?
Looking into what React Concurrent Rendering is, what hooks like useTransition and useDeferredValue do, what are the benefits and downsides of using them.
👍6🔥4