useContext: доступ к контексту
useContext читает значение из React-контекста. Контекст позволяет передавать данные через дерево компонентов без явной передачи через пропсы.
Проблема
Если данные нужны глубоко в дереве, их приходится прокидывать через все промежуточные компоненты:
<App user={user}>
<Layout user={user}>
<Sidebar user={user}>
<Profile user={user} />
Это называется prop drilling. Контекст решает эту проблему.
Создание контекста
import { createContext } from 'react';
type User = {
id: number;
name: string;
};
type AuthContextValue = {
user: User | null;
login: (user: User) => void;
logout: () => void;
};
export const AuthContext = createContext<AuthContextValue | null>(null);
Провайдер
Провайдер оборачивает часть дерева и предоставляет значение:
function AuthProvider({ children }: { children: React.ReactNode }) {
const [user, setUser] = useState<User | null>(null);
const login = (user: User) => setUser(user);
const logout = () => setUser(null);
return (
<AuthContext.Provider value={{ user, login, logout }}>
{children}
</AuthContext.Provider>
);
}
Оборачиваем приложение:
<AuthProvider>
<App />
</AuthProvider>
Использование
import { useContext } from 'react';
function Profile() {
const auth = useContext(AuthContext);
if (!auth?.user) return <p>Войдите</p>;
return <p>{auth.user.name}</p>;
}
Кастомный хук
Часто контекст оборачивают в хук с проверкой:
export function useAuth() {
const context = useContext(AuthContext);
if (!context) {
throw new Error('useAuth must be used within AuthProvider');
}
return context;
}
Использование чище:
function Profile() {
const { user } = useAuth();
if (!user) return <p>Войдите</p>;
return <p>{user.name}</p>;
}
Значение по умолчанию
const ThemeContext = createContext<'light' | 'dark'>('light');
Если провайдера нет — вернётся значение по умолчанию.
Ре-рендеры
Когда значение контекста меняется — все потребители перерисовываются. Если в контексте объект, создаваемый в провайдере, он меняется при каждом рендере и вызывает перерисовку всех потребителей.
Решение — мемоизация:
const value = useMemo(
() => ({ user, login, logout }),
[user]
);
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
Несколько контекстов
Можно вкладывать:
<AuthProvider>
<ThemeProvider>
<App />
</ThemeProvider>
</AuthProvider>
Когда использовать
- Тема (светлая/тёмная)
- Локаль (язык)
- Данные пользователя
- Настройки приложения
- Корзина покупок
Не для всего: если данные нужны только на одном уровне — передавай пропсы.
Итоги
useContextчитает значение контекста- Контекст решает проблему prop drilling
- Провайдер оборачивает дерево
- Значение мемоизируй через
useMemo - Кастомный хук с проверкой удобнее
- Не для всех данных — только для общих