Оптимізація React-додатків: Використовуємо useTransition, useDeferredValue та useOptimistic для плавного UI

Сучасні React-додатки часто мають складний інтерфейс із динамічними елементами, що взаємодіють із користувачем у реальному часі. Оптимізація продуктивності таких додатків є важливою задачею, особливо для забезпечення плавного користувацького досвіду. У цьому контексті React надає корисні інструменти: useTransition, useDeferredValue та useOptimistic. Розглянемо, як їх використовувати.

Що таке useTransition?

useTransition — це хук, який дозволяє позначати частину оновлень у React як “незначущі”. Це означає, що такі оновлення не будуть блокувати основний інтерфейс користувача, навіть якщо вони займають більше часу.

Приклад використання useTransition

Уявімо, що у нас є додаток із великим списком, який користувач може фільтрувати:

import { useState, useTransition } from 'react';

function FilterableList({ items }) {
  const [filter, setFilter] = useState('');
  const [filteredItems, setFilteredItems] = useState(items);
  const [isPending, startTransition] = useTransition();

  const handleFilterChange = (e) => {
    const value = e.target.value;
    setFilter(value);

    startTransition(() => {
      const newFilteredItems = items.filter(item => item.includes(value));
      setFilteredItems(newFilteredItems);
    });
  };

  return (
    <div>
      <input value={filter} onChange={handleFilterChange} placeholder="Фільтрувати..." />
      {isPending && <div>Завантаження...</div>}
      <ul>
        {filteredItems.map((item, index) => <li key={index}>{item}</li>)}
      </ul>
    </div>
  );
}

Як це працює

  • startTransition використовується для запуску “незначущих” оновлень.
  • isPending показує, чи триває транзакція.
  • Основний інтерфейс, наприклад, введення тексту у поле, залишається плавним, навіть якщо фільтрація займає багато часу.

Що таке useDeferredValue?

useDeferredValue дозволяє відкладати оновлення значення до моменту, коли браузер буде готовий виконати його, не впливаючи на продуктивність основного інтерфейсу.

Приклад використання useDeferredValue

Припустимо, що у нас є компонент для пошуку, який відображає результати в режимі реального часу:

import { useState, useDeferredValue } from 'react';

function Search({ items }) {
  const [query, setQuery] = useState('');
  const deferredQuery = useDeferredValue(query);

  const results = items.filter(item => item.includes(deferredQuery));

  return (
    <div>
      <input
        value={query}
        onChange={(e) => setQuery(e.target.value)}
        placeholder="Пошук..."
      />
      <ul>
        {results.map((item, index) => <li key={index}>{item}</li>)}
      </ul>
    </div>
  );
}

Як це працює

  • useDeferredValue створює відкладену версію значення query.
  • Це дозволяє браузеру спочатку обробити термінові оновлення, такі як введення тексту, а вже потім виконати пошук.

Що таке useOptimistic?

useOptimistic — це хук, який допомагає створювати оптимістичні оновлення стану. Це означає, що зміни в UI будуть відображені одразу, ще до завершення операції (наприклад, запиту на сервер).

Приклад використання useOptimistic

Припустимо, у нас є кнопка, яка додає елемент до списку:

import { useOptimistic } from 'react';

function OptimisticList() {
  const [list, setList] = useOptimistic([], (prev, newItem) => [...prev, newItem]);

  const handleAddItem = async () => {
    const newItem = `Item ${list.length + 1}`;
    setList(newItem);

    // Симуляція запиту на сервер
    await fetch('/api/add-item', {
      method: 'POST',
      body: JSON.stringify({ item: newItem }),
    });
  };

  return (
    <div>
      <button onClick={handleAddItem}>Додати елемент</button>
      <ul>
        {list.map((item, index) => <li key={index}>{item}</li>)}
      </ul>
    </div>
  );
}

Як це працює

  • useOptimistic дозволяє відобразити новий елемент у списку одразу після натискання кнопки.
  • Якщо сервер поверне помилку, ви можете відкотити зміни.

Коли використовувати ці інструменти?

  • useTransition: коли ви хочете розділити важкі обчислення або оновлення стану від термінових подій, таких як введення тексту.
  • useDeferredValue: для покращення продуктивності компонентів, які залежать від значення, що часто змінюється.
  • useOptimistic: для створення швидкої та інтерактивної взаємодії з користувачем у ситуаціях, коли серверна операція може займати час.

Висновок

React пропонує потужні інструменти для оптимізації взаємодії з користувачем, особливо у додатках, де продуктивність є ключовою. Використання useTransition, useDeferredValue та useOptimistic дозволяє забезпечити плавний та швидкий UI, одночасно спрощуючи обробку складних обчислень та запитів. Застосовуючи ці інструменти у ваших проектах, ви зможете значно покращити користувацький досвід та продуктивність вашого додатку.