React Junior
207 subscribers
37 photos
462 links
Изучение React с нуля
加入频道
React useTransition: решает все проблемы с производительностью или нет?

Статья (англ.): 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 #важно
👍6🔥4