useTransition — это хук в React, который позволяет помечать обновления состояния как переходы, что помогает улучшить пользовательский опыт, сохраняя отзывчивость интерфейса во время дорогих операций. Он разделяет срочные обновления (например, ввод текста) и несрочные обновления (например, фильтрацию списка).
Синтаксис: const [isPending, startTransition] = useTransition();
Ключевые особенности:
Пример использования:
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 возвращает массив с двумя значениями:
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:
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>
);
}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>
);
}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 когда:
function ExpensiveOperationComponent({ data }) {
const [searchTerm, setSearchTerm] = useState('');
const [isPending, startTransition] = useTransition();
const handleSearch = (term) => {
setSearchTerm(term);
// Сохраняем отзывчивость интерфейса во время поиска
startTransition(() => {
performExpensiveSearch(data, term);
});
};
return (
<div>
<SearchInput onSearch={handleSearch} />
{isPending && <LoadingIndicator />}
<Results />
</div>
);
}❌ Избегайте useTransition когда:
// ❌ Не стоит использовать 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>;
}// ❌ Избыточное использование
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>;
}// ❌ Ошибка: обертывание срочных обновлений
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} />;
}// ❌ Нет обратной связи во время перехода
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 для:
✅ Когда использовать:
❌ Когда избегать:
✅ Лучшие практики:
useTransition — мощный инструмент для улучшения пользовательского опыта, но его следует использовать обдуманно. Определите реальные узкие места производительности и применяйте useTransition для их решения. 🚀
Хотите больше статей для подготовки к собеседованиям? Подписывайтесь на EasyAdvice, добавляйте сайт в закладки и совершенствуйтесь каждый день 💪