Расскажите про хук - useTransition, как он работает, где применяется и для чего?

👨‍💻 Frontend Developer 🟠 Может встретиться 🎚️ Средний
#React #Hooks

Краткий ответ

useTransition — это хук в React, который позволяет помечать обновления состояния как переходы, что помогает улучшить пользовательский опыт, сохраняя отзывчивость интерфейса во время дорогих операций. Он разделяет срочные обновления (например, ввод текста) и несрочные обновления (например, фильтрацию списка).

Синтаксис: const [isPending, startTransition] = useTransition();

Ключевые особенности:

  • Улучшает отзывчивость интерфейса во время дорогих операций
  • Разделяет срочные и несрочные обновления состояния
  • Предоставляет визуальную обратную связь через состояние isPending
  • Работает с функциями параллельного рендеринга

Пример использования:

function SearchComponent() {
  const [query, setQuery] = useState('');
  const [isPending, startTransition] = useTransition();
  
  const handleSearch = (e) => {
    const value = e.target.value;
    setQuery(value);
    
    startTransition(() => {
      // Дорогая операция фильтрации
      setSearchResults(filterData(value));
    });
  };
  
  return (
    <div>
      <input onChange={handleSearch} value={query} />
      {isPending && <div>Загрузка...</div>}
    </div>
  );
}

Полный ответ

Хук useTransition — один из встроенных хуков React, предназначенный для улучшения пользовательского опыта путем пометки обновлений состояния как переходов. Он помогает сохранить отзывчивость интерфейса во время дорогих операций, разделяя срочные обновления (например, ввод пользователя) и несрочные обновления (например, фильтрацию или сортировку данных). 🚀

Как работает useTransition?

useTransition возвращает массив с двумя значениями:

  1. isPending — булево значение, указывающее на активность перехода
  2. startTransition — функция для обертывания несрочных обновлений состояния
import React, { useState, useTransition } from 'react';
 
function Component() {
  const [isPending, startTransition] = useTransition();
  const [data, setData] = useState([]);
  
  const handleFilter = (filterValue) => {
    // Срочное обновление - немедленная обратная связь
    // setFilter(filterValue);
    
    // Несрочное обновление - обернуто в переход
    startTransition(() => {
      setData(filterExpensiveData(filterValue));
    });
  };
  
  return (
    <div>
      {isPending ? <div>Обновление...</div> : <DataList data={data} />}
    </div>
  );
}

Когда вызывается startTransition:

  1. React помечает обновление как несрочное
  2. Срочные обновления продолжают отвечать немедленно
  3. Несрочные обновления обрабатываются в фоне
  4. isPending становится true во время обработки

Основные применения useTransition

1. Улучшение опыта поиска

function SearchableList({ items }) {
  const [query, setQuery] = useState('');
  const [filteredItems, setFilteredItems] = useState(items);
  const [isPending, startTransition] = useTransition();
  
  const handleSearch = (e) => {
    const value = e.target.value;
    setQuery(value);
    
    // Сохраняем отзывчивость ввода, пока фильтрация происходит в фоне
    startTransition(() => {
      setFilteredItems(
        items.filter(item => 
          item.name.toLowerCase().includes(value.toLowerCase())
        )
      );
    });
  };
  
  return (
    <div>
      <input 
        value={query} 
        onChange={handleSearch} 
        placeholder="Поиск элементов..."
      />
      {isPending && <div>Поиск...</div>}
      <ItemList items={filteredItems} />
    </div>
  );
}

2. Переключение вкладок с дорогим контентом

function TabComponent() {
  const [tab, setTab] = useState('home');
  const [isPending, startTransition] = useTransition();
  
  const switchTab = (newTab) => {
    // Немедленная визуальная обратная связь
    startTransition(() => {
      // Дорогостоящий рендеринг контента вкладки
      setTab(newTab);
    });
  };
  
  return (
    <div>
      <nav>
        <button onClick={() => switchTab('home')}>Главная</button>
        <button onClick={() => switchTab('profile')}>Профиль</button>
        <button onClick={() => switchTab('settings')}>Настройки</button>
      </nav>
      
      {isPending && <div>Загрузка вкладки...</div>}
      <TabContent tab={tab} />
    </div>
  );
}

3. Фильтрация больших наборов данных

function DataFilter({ largeDataset }) {
  const [filter, setFilter] = useState('');
  const [isPending, startTransition] = useTransition();
  
  const [filteredData, setFilteredData] = useState(largeDataset);
  
  const applyFilter = (newFilter) => {
    setFilter(newFilter);
    
    startTransition(() => {
      const result = largeDataset.filter(item => 
        item.category.includes(newFilter)
      );
      setFilteredData(result);
    });
  };
  
  return (
    <div>
      <input 
        value={filter} 
        onChange={(e) => applyFilter(e.target.value)} 
        placeholder="Фильтр по категории..."
      />
      {isPending ? <div>Фильтрация...</div> : <DataGrid data={filteredData} />}
    </div>
  );
}

Когда лучше использовать useTransition

Используйте useTransition когда:

  1. Дорогие обновления состояния — фильтрация, сортировка больших наборов данных
  2. Обработка пользовательского ввода — сохранение отзывчивости во время операций
  3. Переключение вкладок или представлений — когда рендеринг контента дорогой
  4. Операции поиска — сохранение отзывчивости во время фильтрации
function ExpensiveOperationComponent({ data }) {
  const [searchTerm, setSearchTerm] = useState('');
  const [isPending, startTransition] = useTransition();
  
  const handleSearch = (term) => {
    setSearchTerm(term);
    
    // Сохраняем отзывчивость интерфейса во&nbsp;время поиска
    startTransition(() => {
      performExpensiveSearch(data, term);
    });
  };
  
  return (
    <div>
      <SearchInput onSearch={handleSearch} />
      {isPending && <LoadingIndicator />}
      <Results />
    </div>
  );
}

Когда лучше отказаться от useTransition

Избегайте useTransition когда:

  1. Простые обновления состояния — базовые формы, простые переключатели
  2. Требуется немедленная обратная связь — когда пользователям нужны мгновенные результаты
  3. Небольшие наборы данных — для небольших списков или быстрых операций
  4. Когда производительность не критична — преждевременная оптимизация
// ❌ Не стоит использовать useTransition без необходимости
function SimpleToggle() {
  const [isVisible, setIsVisible] = useState(true);
  const [, startTransition] = useTransition(); // Избыточно!
  
  const toggle = () => {
    startTransition(() => {
      setIsVisible(!isVisible); // Простое переключение не нуждается в переходе
    });
  };
  
  return <button onClick={toggle}>{isVisible ? 'Скрыть' : 'Показать'}</button>;
}
 
// ✅ Просто используйте обычное обновление состояния
function SimpleToggle() {
  const [isVisible, setIsVisible] = useState(true);
  
  const toggle = () => {
    setIsVisible(!isVisible);
  };
  
  return <button onClick={toggle}>{isVisible ? 'Скрыть' : 'Показать'}</button>;
}

Распространенные ошибки

1. Использование useTransition без необходимости

// ❌ Избыточное использование
function Component() {
  const [count, setCount] = useState(0);
  const [, startTransition] = useTransition();
  
  const increment = () => {
    startTransition(() => {
      setCount(c => c + 1); // Простое увеличение не нуждается в переходе
    });
  };
  
  return <button onClick={increment}>Счетчик: {count}</button>;
}
 
// ✅ Просто используйте обычное обновление состояния
function Component() {
  const [count, setCount] = useState(0);
  
  const increment = () => {
    setCount(c => c + 1);
  };
  
  return <button onClick={increment}>Счетчик: {count}</button>;
}

2. Обертывание срочных обновлений в переходы

// ❌ Ошибка: обертывание срочных обновлений
function SearchComponent() {
  const [query, setQuery] = useState('');
  const [, startTransition] = useTransition();
  
  const handleSearch = (e) => {
    const value = e.target.value;
    
    startTransition(() => {
      setQuery(value); // Должно быть немедленным для обратной связи при вводе
    });
  };
  
  return <input value={query} onChange={handleSearch} />;
}
 
// ✅ Правильно: разделение срочных и несрочных обновлений
function SearchComponent() {
  const [query, setQuery] = useState('');
  const [results, setResults] = useState([]);
  const [, startTransition] = useTransition();
  
  const handleSearch = (e) => {
    const value = e.target.value;
    
    // Срочное обновление для немедленной обратной связи при вводе
    setQuery(value);
    
    // Несрочное обновление для дорогой фильтрации
    startTransition(() => {
      setResults(filterResults(value));
    });
  };
  
  return <input value={query} onChange={handleSearch} />;
}

3. Отсутствие обратной связи пользователю

// ❌ Нет обратной связи во время перехода
function Component() {
  const [data, setData] = useState([]);
  const [isPending, startTransition] = useTransition();
  
  const loadData = () => {
    startTransition(() => {
      setData(fetchExpensiveData());
    });
  };
  
  return (
    <div>
      <button onClick={loadData}>Загрузить данные</button>
      {/* Пользователь не знает, что операция выполняется */}
      <DataDisplay data={data} />
    </div>
  );
}
 
// ✅ Предоставление визуальной обратной связи
function Component() {
  const [data, setData] = useState([]);
  const [isPending, startTransition] = useTransition();
  
  const loadData = () => {
    startTransition(() => {
      setData(fetchExpensiveData());
    });
  };
  
  return (
    <div>
      <button onClick={loadData}>Загрузить данные</button>
      {isPending && <div className="loading">Загрузка...</div>}
      <DataDisplay data={data} />
    </div>
  );
}

Резюме

useTransition — это хук React для:

  • Улучшения отзывчивости интерфейса во время дорогих операций
  • Разделения срочных обновлений и несрочных обновлений
  • Обеспечения лучшего пользовательского опыта с визуальной обратной связью
  • Работы с функциями параллельного рендеринга

Когда использовать:

  • Дорогие обновления состояния (фильтрация, сортировка больших наборов данных)
  • Обработка пользовательского ввода (сохранение отзывчивости при вводе)
  • Переключение вкладок или представлений с дорогим контентом
  • Операции поиска, требующие фильтрации

Когда избегать:

  • Простые обновления состояния (базовые переключатели, формы)
  • Когда требуется немедленная обратная связь
  • Небольшие наборы данных или быстрые операции
  • Когда оптимизация производительности не нужна

Лучшие практики:

  • Используйте только при наличии дорогих операций
  • Всегда предоставляйте визуальную обратную связь через isPending
  • Разделяйте срочные обновления и несрочные обновления
  • Не используйте для простых изменений состояния

useTransition — мощный инструмент для улучшения пользовательского опыта, но его следует использовать обдуманно. Определите реальные узкие места производительности и применяйте useTransition для их решения. 🚀


Хотите больше статей для подготовки к собеседованиям? Подписывайтесь на EasyAdvice, добавляйте сайт в закладки и совершенствуйтесь каждый день 💪