useContext — это хук в React, который позволяет получать значения из React Context! Он предоставляет способ передачи данных глубоко вниз по дереву компонентов без пропс-дриллинга.
Ключевые особенности:
React.createContext()Пример использования:
import React, { createContext, useContext } from 'react';
const ThemeContext = createContext('light');
function App() {
return (
<ThemeContext.Provider value="dark">
<Toolbar />
</ThemeContext.Provider>
);
}
function Toolbar() {
return (
<div>
<ThemedButton />
</div>
);
}
function ThemedButton() {
const theme = useContext(ThemeContext);
return (
<button className={theme}>
Кнопка с темой {theme}
</button>
);
}Хук useContext — это один из встроенных хуков React, который предоставляет способ подписки на контекст и получения его значений в функциональных компонентах. Это современная альтернатива старому API с Consumer, которая делает код более читаемым и лаконичным. 🌟
useContext принимает объект контекста (значение, возвращаемое из React.createContext) и возвращает текущее значение контекста для этого контекста:
const value = useContext(MyContext);Компонент, вызывающий useContext, всегда будет перерисовываться при изменении значения контекста. React берет текущее значение контекста из ближайшего соответствующего Provider над компонентом в дереве.
import React, { createContext, useContext } from 'react';
// 1. Создаем контекст с начальным значением
const UserContext = createContext({
name: 'Гость',
isLoggedIn: false
});
// 2. Компонент-провайдер
function UserProvider({ children }) {
const [user, setUser] = React.useState({
name: 'Александр',
isLoggedIn: true
});
return (
<UserContext.Provider value={user}>
{children}
</UserContext.Provider>
);
}
// 3. Компонент, использующий контекст
function UserProfile() {
const user = useContext(UserContext);
return (
<div>
<h1>Привет, {user.name}!</h1>
<p>Статус: {user.isLoggedIn ? 'Авторизован' : 'Гость'}</p>
</div>
);
}
// 4. Использование в приложении
function App() {
return (
<UserProvider>
<UserProfile />
</UserProvider>
);
}import React, { createContext, useContext, useState } from 'react';
const ThemeContext = createContext();
function ThemeProvider({ children }) {
const [theme, setTheme] = useState('light');
const toggleTheme = () => {
setTheme(prev => prev === 'light' ? 'dark' : 'light');
};
return (
<ThemeContext.Provider value={{ theme, toggleTheme }}>
{children}
</ThemeContext.Provider>
);
}
function ThemedButton() {
const { theme, toggleTheme } = useContext(ThemeContext);
return (
<button
className={`btn btn-${theme}`}
onClick={toggleTheme}
>
Переключить на {theme === 'light' ? 'темную' : 'светлую'} тему
</button>
);
}
function App() {
return (
<ThemeProvider>
<div className="app">
<h1>Приложение с темизацией</h1>
<ThemedButton />
</div>
</ThemeProvider>
);
}import React, { createContext, useContext, useReducer } from 'react';
const AuthContext = createContext();
const authReducer = (state, action) => {
switch (action.type) {
case 'LOGIN':
return {
user: action.payload,
isAuthenticated: true
};
case 'LOGOUT':
return {
user: null,
isAuthenticated: false
};
default:
return state;
}
};
function AuthProvider({ children }) {
const [state, dispatch] = useReducer(authReducer, {
user: null,
isAuthenticated: false
});
const login = (userData) => {
dispatch({ type: 'LOGIN', payload: userData });
};
const logout = () => {
dispatch({ type: 'LOGOUT' });
};
return (
<AuthContext.Provider value={{ ...state, login, logout }}>
{children}
</AuthContext.Provider>
);
}
function UserProfile() {
const { user, isAuthenticated, logout } = useContext(AuthContext);
if (!isAuthenticated) {
return <div>Пожалуйста, войдите в систему</div>;
}
return (
<div>
<h2>Привет, {user.name}!</h2>
<button onClick={logout}>Выйти</button>
</div>
);
}import React, { createContext, useContext } from 'react';
const translations = {
en: {
greeting: 'Hello',
welcome: 'Welcome to our app'
},
ru: {
greeting: 'Привет',
welcome: 'Добро пожаловать в наше приложение'
}
};
const LanguageContext = createContext();
function LanguageProvider({ children, defaultLanguage = 'en' }) {
const [language, setLanguage] = React.useState(defaultLanguage);
const t = (key) => {
return translations[language]?.[key] || key;
};
return (
<LanguageContext.Provider value={{ language, setLanguage, t }}>
{children}
</LanguageContext.Provider>
);
}
function Greeting() {
const { t, language, setLanguage } = useContext(LanguageContext);
return (
<div>
<h1>{t('greeting')}!</h1>
<p>{t('welcome')}</p>
<select
value={language}
onChange={(e) => setLanguage(e.target.value)}
>
<option value="en">English</option>
<option value="ru">Русский</option>
</select>
</div>
);
}// ❌ Без контекста - пропс-дриллинга
function App() {
const theme = 'dark';
return <Layout theme={theme} />;
}
function Layout({ theme }) {
return <Header theme={theme} />;
}
function Header({ theme }) {
return <Button theme={theme} />;
}
function Button({ theme }) {
return <button className={`btn-${theme}`}>Кнопка</button>;
}
// ✅ С контекстом - прямой доступ
const ThemeContext = createContext('light');
function App() {
return (
<ThemeContext.Provider value="dark">
<Layout />
</ThemeContext.Provider>
);
}
function Layout() {
return <Header />;
}
function Header() {
return <Button />;
}
function Button() {
const theme = useContext(ThemeContext);
return <button className={`btn-${theme}`}>Кнопка</button>;
}// ❌ Сложный API с множеством пропсов
function ComplexComponent({
theme,
locale,
permissions,
userSettings,
// ... еще 10 пропсов
}) {
// ...
}
// ✅ Чистый API с контекстом
function CleanComponent() {
// Получаем все нужные данные из контекстов
const theme = useContext(ThemeContext);
const locale = useContext(LocaleContext);
const permissions = useContext(PermissionsContext);
const userSettings = useContext(UserSettingsContext);
// ...
}✅ Используйте useContext когда:
// Хорошее применение useContext
const AppSettingsContext = createContext();
function App() {
return (
<AppSettingsContext.Provider value={{
theme: 'dark',
language: 'ru',
notifications: true
}}>
<MainLayout />
</AppSettingsContext.Provider>
);
}
function NotificationPanel() {
const { notifications } = useContext(AppSettingsContext);
if (!notifications) return null;
return <div>Панель уведомлений</div>;
}❌ Избегайте useContext когда:
// Плохое применение useContext
const ExpensiveDataContext = createContext();
// ❌ Контекст обновляется слишком часто
function DataProvider({ children }) {
const [data, setData] = useState([]);
// Обновление данных каждую секунду
useEffect(() => {
const interval = setInterval(() => {
setData(prev => [...prev, Date.now()]);
}, 1000);
return () => clearInterval(interval);
}, []);
return (
<ExpensiveDataContext.Provider value={data}>
{children}
</ExpensiveDataContext.Provider>
);
}
// Все компоненты будут перерисовываться каждую секунду
function ExpensiveComponent() {
const data = useContext(ExpensiveDataContext);
// Дорогостоящая операция с данными
return <div>{JSON.stringify(data)}</div>;
}// ❌ Один большой контекст
const AppContext = createContext();
function AppProvider({ children }) {
const [theme, setTheme] = useState('light');
const [user, setUser] = useState(null);
const [notifications, setNotifications] = useState([]);
return (
<AppContext.Provider value={{
theme, setTheme,
user, setUser,
notifications, setNotifications
}}>
{children}
</AppContext.Provider>
);
}
// ✅ Разделенные контексты
const ThemeContext = createContext();
const UserContext = createContext();
const NotificationsContext = createContext();
function ThemeProvider({ children }) {
const [theme, setTheme] = useState('light');
return (
<ThemeContext.Provider value={{ theme, setTheme }}>
{children}
</ThemeContext.Provider>
);
}
function UserProvider({ children }) {
const [user, setUser] = useState(null);
return (
<UserContext.Provider value={{ user, setUser }}>
{children}
</UserContext.Provider>
);
}
// Компоненты перерисовываются только при изменении своих данных
function ThemeToggle() {
const { theme, setTheme } = useContext(ThemeContext);
// Перерисовывается только при изменении темы
}
function UserProfile() {
const { user } = useContext(UserContext);
// Перерисовывается только при изменении пользователя
}function ThemeProvider({ children }) {
const [theme, setTheme] = useState('light');
const [accentColor, setAccentColor] = useState('blue');
// ❌ Новое значение объекта при каждом рендере
const contextValue = {
theme,
setTheme,
accentColor,
setAccentColor
};
// ✅ Мемоизация значения контекста
const contextValue = React.useMemo(() => ({
theme,
setTheme,
accentColor,
setAccentColor
}), [theme, setTheme, accentColor, setAccentColor]);
return (
<ThemeContext.Provider value={contextValue}>
{children}
</ThemeContext.Provider>
);
}// Пользовательский хук для работы с контекстом
function useTheme() {
const context = useContext(ThemeContext);
if (!context) {
throw new Error('useTheme must be used within ThemeProvider');
}
return context;
}
// Использование
function ThemedButton() {
const { theme, toggleTheme } = useTheme();
return (
<button
className={`btn-${theme}`}
onClick={toggleTheme}
>
Переключить тему
</button>
);
}// ❌ Ошибка: использование контекста без провайдера
const ThemeContext = createContext('light');
function Button() {
const theme = useContext(ThemeContext); // Будет 'light' (значение по умолчанию)
return <button className={theme}>Кнопка</button>;
}
// ✅ Правильно: использование с провайдером
function App() {
return (
<ThemeContext.Provider value="dark">
<Button />
</ThemeContext.Provider>
);
}// ❌ Ошибка: изменение объекта контекста напрямую
const UserContext = createContext();
function UserProvider({ children }) {
const [user, setUser] = useState({ name: 'Иван', age: 30 });
// Неправильное изменение объекта
const updateUser = (field, value) => {
user[field] = value; // Мутирует состояние напрямую
setUser(user); // Не вызовет перерисовку
};
return (
<UserContext.Provider value={{ user, updateUser }}>
{children}
</UserContext.Provider>
);
}
// ✅ Правильно: создание нового объекта
function UserProvider({ children }) {
const [user, setUser] = useState({ name: 'Иван', age: 30 });
const updateUser = (field, value) => {
setUser(prev => ({ ...prev, [field]: value })); // Создает новый объект
};
return (
<UserContext.Provider value={{ user, updateUser }}>
{children}
</UserContext.Provider>
);
}// ❌ Ошибка: создание контекста внутри компонента
function App() {
// Контекст создается при каждом рендере
const ThemeContext = createContext('light');
return (
<ThemeContext.Provider value="dark">
<Button />
</ThemeContext.Provider>
);
}
// ✅ Правильно: создание контекста на верхнем уровне
const ThemeContext = createContext('light');
function App() {
return (
<ThemeContext.Provider value="dark">
<Button />
</ThemeContext.Provider>
);
}// ❌ Ошибка: использование хука в условии
function Component() {
if (someCondition) {
const value = useContext(MyContext); // Ошибка правил хуков!
// ...
}
// ...
}
// ✅ Правильно: всегда вызывайте хуки на верхнем уровне
function Component() {
const value = useContext(MyContext); // Правильно
if (someCondition) {
// Используйте value внутри условия
// ...
}
// ...
}| Критерий | useContext | Redux |
|---|---|---|
| Сложность | Простая | Сложная |
| Размер бандла | 0 (встроенный) | ~2KB |
| Отладка | Ограниченная | Расширенная (Redux DevTools) |
| Масштабируемость | Ограниченная | Высокая |
| Кривая обучения | Низкая | Высокая |
| Подходит для | Небольших приложений | Сложных приложений |
// useContext для простого приложения
const AppStateContext = createContext();
function AppStateProvider({ children }) {
const [state, setState] = useState({
user: null,
theme: 'light',
notifications: []
});
return (
<AppStateContext.Provider value={[state, setState]}>
{children}
</AppStateContext.Provider>
);
}
// Redux для сложного приложения
import { createStore } from 'redux';
import { useSelector, useDispatch } from 'react-redux';
const store = createStore(rootReducer);
function UserProfile() {
const user = useSelector(state => state.user);
const dispatch = useDispatch();
// ...
}| Критерий | useContext | Пропсы |
|---|---|---|
| Читаемость | Высокая (меньше кода) | Средняя (много пропсов) |
| Явность | Низкая (зависимости скрыты) | Высокая (все явно передается) |
| Гибкость | Ограниченная | Высокая |
| Производительность | Средняя | Высокая |
| Поддержка | Средняя | Высокая |
// Пропсы - явная передача данных
function Parent() {
const theme = 'dark';
return <Child theme={theme} />;
}
function Child({ theme }) {
return <GrandChild theme={theme} />;
}
function GrandChild({ theme }) {
return <button className={theme}>Кнопка</button>;
}
// useContext - неявная передача данных
const ThemeContext = createContext('light');
function Parent() {
return (
<ThemeContext.Provider value="dark">
<Child />
</ThemeContext.Provider>
);
}
function Child() {
return <GrandChild />;
}
function GrandChild() {
const theme = useContext(ThemeContext);
return <button className={theme}>Кнопка</button>;
}✅ useContext — это хук React для:
✅ Когда использовать:
❌ Когда избегать:
✅ Лучшие практики:
useMemo для мемоизации значений контекстаuseContext — мощный инструмент для управления состоянием в React-приложениях, который помогает избежать пропс-дриллинга и делает код более читаемым. Однако важно понимать, когда его использовать, а когда предпочесть более специализированные решения. 🚀
Хотите больше статей для подготовки к собеседованиям? Подписывайтесь на EasyAdvice, добавляйте сайт в закладки и совершенствуйтесь каждый день 💪