Пошук уроків, статей та іншого контенту
Використаєте хук use для читання промісів і контексту та побудови асинхронних компонентів.
useuse — це React API, який дає змогу читати значення промісу або контексту безпосередньо під час рендерингу компонента.
import { use } from 'react';Хук доступний у React 19 і новіших версіях.
За допомогою use можна:
отримувати результат промісу;
читати значення контексту;
призупиняти рендеринг компонента до завершення промісу;
Коли проміс ще не завершився, React призупиняє компонент і показує найближчий Suspense fallback.
useРозглянемо проміс, який імітує запит до сервера:
import { use } from 'react';
const userPromise = new Promise((resolve) => {
setTimeout(() => {
resolve({
name: 'Олена',
role: 'Frontend Developer',
});
}, 1500);
});
function UserProfile() {
const user = use(userPromise);
return (
<section>
<h2>{user.name}</h2>
<p>{user.role}</p>
</section>
);
}Якщо проміс ще не завершений, виклик use(userPromise) призупиняє UserProfile. React продовжить його рендеринг після виконання промісу.
Щоб показати інтерфейс завантаження, компонент потрібно обгорнути в Suspense:
import { Suspense } from 'react';
function App() {
return (
<Suspense fallback={<p>Завантаження профілю...</p>}>
<UserProfile />
</Suspense>
);
}Поки userPromise очікує результат, користувач побачить:
Завантаження профілю...Після виконання промісу React відрендерить профіль.
Проміс не варто створювати безпосередньо під час кожного рендерингу компонента:
function UserProfile() {
// Поганий приклад
const userPromise = fetch('/api/user').then((response) => response.json());
const user = use(userPromise);
return <h2>{user.name}</h2>;
}Кожен рендеринг створює новий проміс. Якщо компонент призупиняється, React може повторити його рендеринг, що створить ще один проміс. Це може призвести до нескінченних повторних запитів або постійного завантаження.
Проміс потрібно створити в місці, де він зберігатиме стабільне посилання:
const userPromise = fetch('/api/user').then((response) => response.json());
function UserProfile() {
const user = use(userPromise);
return <h2>{user.name}</h2>;
}У реальному застосунку для цього також використовують стабільний кеш даних або бібліотеку для роботи із серверним станом.
Suspense і помилки промісуSuspense обробляє стан очікування, але не є обробником помилок. Якщо проміс буде відхилено, помилку має перехопити Error Boundary.
import { Component, Suspense, use } from 'react';
const userPromise = Promise.reject(new Error('Не вдалося завантажити користувача'));
class ErrorBoundary extends Component {
state = {
error: null,
};
static getDerivedStateFromError(error) {
return { error };
}
render() {
if (this.state.error) {
return <p>Помилка: {this.state.error.message}</p>;
}
return this.props.children;
}
}
function UserProfile() {
const user = use(userPromise);
return <h2>{user.name}</h2>;
}
function App() {
return (
<ErrorBoundary>
<Suspense fallback={<p>Завантаження...</p>}>
<UserProfile />
</Suspense>
</ErrorBoundary>
);
}Порядок обгортання має значення:
Suspense показує стан очікування;
ErrorBoundary показує стан помилки.
useuse також може читати значення React Context.
Створимо контекст теми:
import { createContext, use } from 'react';
const ThemeContext = createContext('light');
function Button() {
const theme = use(ThemeContext);
return (
<button className={`button button-${theme}`}>
Зберегти
</button>
);
}Передамо значення через Provider:
function App() {
return (
<ThemeContext.Provider value="dark">
<Button />
</ThemeContext.Provider>
);
}Компонент Button отримає значення dark.
Якщо поблизу немає ThemeContext.Provider, буде використано значення за замовчуванням, передане в createContext:
const ThemeContext = createContext('light');Звичайні хуки не можна викликати всередині умов:
if (isAdmin) {
const value = useContext(Context); // Неправильно
}Для use це обмеження не діє. Його можна викликати в умові:
import { createContext, use } from 'react';
const UserContext = createContext(null);
function UserActions({ showActions }) {
if (!showActions) {
return <p>Дії недоступні</p>;
}
const user = use(UserContext);
return <p>Дії користувача: {user.name}</p>;
}Це одна з важливих відмінностей use від більшості інших React-хуків.
Втім, use не можна викликати всередині try/catch:
function Component() {
try {
const value = use(SomeContext); // Неправильно
return <p>{value}</p>;
} catch {
return <p>Помилка</p>;
}
}Для обробки помилок промісів використовуйте Error Boundary.
У компоненті можна одночасно читати проміс і контекст:
import { createContext, Suspense, use } from 'react';
const ThemeContext = createContext('light');
const productPromise = new Promise((resolve) => {
setTimeout(() => {
resolve({
name: 'Механічна клавіатура',
price: 3200,
});
}, 1000);
});
function ProductCard() {
const product = use(productPromise);
const theme = use(ThemeContext);
return (
<article className={`product product-${theme}`}>
<h2>{product.name}</h2>
<p>Ціна: {product.price} грн</p>
</article>
);
}
function App() {
return (
<ThemeContext.Provider value="dark">
<Suspense fallback={<p>Завантаження товару...</p>}>
<ProductCard />
</Suspense>
</ThemeContext.Provider>
);
}Під час рендерингу ProductCard:
use(productPromise) очікує дані товару;
Suspense показує fallback;
після виконання промісу компонент читає дані;
use(ThemeContext) отримує поточну тему;
компонент відображає готовий інтерфейс.
Нижче наведено приклад, який можна використати у React 19-проєкті.
import {
createContext,
createElement,
Suspense,
use,
useState,
} from 'react';
import { createRoot } from 'react-dom/client';
const ThemeContext = createContext('light');
const userPromise = new Promise((resolve) => {
setTimeout(() => {
resolve({
name: 'Марко',
role: 'React Developer',
});
}, 1200);
});
function UserProfile() {
const user = use(userPromise);
const theme = use(ThemeContext);
return (
<section
style={{
padding: '16px',
border: '1px solid #999',
background: theme === 'dark' ? '#222' : '#fff',
color: theme === 'dark' ? '#fff' : '#222',
}}
>
<h2>{user.name}</h2>
<p>{user.role}</p>
<p>Поточна тема: {theme}</p>
</section>
);
}
function App() {
const [theme, setTheme] = useState('light');
function toggleTheme() {
setTheme((currentTheme) =>
currentTheme === 'light' ? 'dark' : 'light'
);
}
return (
<ThemeContext.Provider value={theme}>
<main style={{ padding: '24px' }}>
<button onClick={toggleTheme}>
Змінити тему
</button>
<div style={{ marginTop: '16px' }}>
<Suspense fallback={<p>Завантаження користувача...</p>}>
<UserProfile />
</Suspense>
</div>
</main>
</ThemeContext.Provider>
);
}
createRoot(document.getElementById('root')).render(<App />);У цьому прикладі:
userPromise стабільно створюється поза компонентом;
UserProfile читає результат промісу через use;
UserProfile читає тему через use(ThemeContext);
Suspense показує fallback під час очікування;
зміна контексту призводить до повторного рендерингу компонента.
use і useEffectДля читання вже підготовленого промісу use часто дає простішу модель, ніж ручне керування станом у useEffect.
Традиційний підхід потребує окремих станів:
function UserProfile() {
const [user, setUser] = useState(null);
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
fetch('/api/user')
.then((response) => response.json())
.then((data) => {
setUser(data);
setIsLoading(false);
})
.catch((requestError) => {
setError(requestError);
setIsLoading(false);
});
}, []);
// Потрібно окремо обробити всі стани
}За допомогою use стани очікування та помилки можуть бути винесені на рівень композиції:
Suspense відповідає за завантаження;
Error Boundary відповідає за помилки;
сам компонент зосереджується на відображенні отриманих даних.
Це особливо корисно для компонентів, які читають дані в межах дерева, що вже підтримує Suspense.
function Component() {
const promise = loadData();
const data = use(promise);
return <p>{data.name}</p>;
}Створюйте проміс стабільно, а не під час кожного рендерингу.
SuspenseЯкщо компонент читає незавершений проміс, для нього має бути доступний батьківський Suspense:
<Suspense fallback={<p>Завантаження...</p>}>
<Component />
</Suspense>Без нього застосунок не зможе коректно показати стан очікування.
Suspense перехопить помилкуSuspense не відображає помилки відхиленого промісу. Для цього потрібен Error Boundary.
use у try/catchuse не можна розміщувати всередині try/catch. Обробляйте помилки на рівні Error Boundary.
Якщо значення Provider — об’єкт, його зміна посилання може спричинити повторний рендеринг споживачів контексту:
<ThemeContext.Provider value={{ theme: 'dark' }}>
<App />
</ThemeContext.Provider>У цьому прикладі новий об’єкт створюється під час кожного рендерингу батьківського компонента. Для простих значень, таких як рядки або числа, така проблема не виникає.
use використовується для читання промісів і контексту.
Для незавершеного промісу React призупиняє компонент.
Suspense показує fallback під час очікування.
Відхилені проміси потрібно обробляти через Error Boundary.
Проміси мають бути стабільними й не створюватися під час кожного рендерингу.
use(Context) може викликатися в умовах і циклах.
use не можна викликати всередині try/catch.
Комбінація use, Suspense і Context дає змогу будувати компоненти, які декларативно працюють з асинхронними даними.