Обновить

Оптимизация React-приложений: Используем useTransition, useDeferredValue и useOptimistic для плавного UI

Уровень сложностиСредний
Время на прочтение8 мин
Охват и читатели24K
Всего голосов 16: ↑16 и ↓0+16
Комментарии8

Комментарии 8

Спасибо за статью, а можно еще сделать разбор новых хуков, которые стали доступны с React 19 помимо useOptimistic?

Спасибо за отзыв!
Да, планирую сделать разбор и остальных новых хуков из React 19, чтобы охватить все интересные возможности (будет отдельная статья). А пока можете заглянуть в мою подробную статью о хуке useActionState: https://habr.com/ru/articles/870216/ - там тоже есть немало интересных идей по оптимизации и управлению состоянием. Спасибо, что читаете!

Подскажите пожалуйста, могу ошибаться, разве в вашем примере с useDeferredValue хуком при изменении состояния не произойдет ререндер как компонента, который содержит стейт поиска, так и дочерний компонент, куда передаётся deffered значение пропом?

Спасибо за коментарий, отличное наблюдение.
useDeferredValue помогает распределять приоритеты рендеринга, чтобы сохранить отзывчивость интерфейса (например, при вводе текста). Однако, как подчёркивает и документация, для полного раскрытия потенциала этого хука тяжёлый компонент желательно обернуть в React.memo. Без мемоизации он будет перерендериваться при каждом обновлении родителя, и все преимущество теряется. Поправлю пример в стате, чтобы сразу показать наглядное использование вместе с memo.
Спасибо, что обратили на это внимание!

Полезная информация для меня, дизайнера

Простите, но кажется пример useTransition некорректный.

Если вы добавите в фильтрацию реальную задержку, то увидите что никакой отзывчивости нет, интерфейс виснет при вводе каждого символа:

const syncSleep = (time: number) => {
  const startTime = new Date();

  while (new Date().getTime() - startTime.getTime() < time) {}
};

// Некритичное обновление вынесем в startTransition
startTransition(() => {
  syncSleep(3000);   // <------- Ставим реальную задержку
  const filtered = allItems.filter((item) =>
    item.text.toLowerCase().includes(value.toLowerCase())
  );
  setFilteredItems(filtered);
});

Причем виснет он даже не на 3 секунды, а на 3 * число введенных символов. Т.е. абсолютно никакого выигрыша.

Можете сами проверить:

https://codesandbox.io/p/devbox/objective-http-ph3lsl?workspaceId=ws_Vn7GNkZok9ujTbLbsTAhER

Время редактирования комментария закончилось, так что приходится писать новый.

Попробовал переписать вот так, опираясь на примеры из документации:

// Некритичное обновление вынесем в startTransition
    startTransition(() => {
      setTimeout(() => {
        syncSleep(3000);
        const filtered = allItems.filter((item) =>
          item.text.toLowerCase().includes(value.toLowerCase())
        );

        startTransition(() => {
          setFilteredItems(filtered);
        });
      }, 0);
    });

Интерфейс все равно подвисает, что понятно, но по крайней мере при быстром вводе символы появляются раньше, чем все дофильтруется.

https://codesandbox.io/p/devbox/peaceful-smoke-4mynhv?file=%2Fsrc%2FApp.js%3A45%2C13&workspaceId=ws_Vn7GNkZok9ujTbLbsTAhER

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации