useContext: базово
Context позволяет передавать данные через дерево компонентов, не прокидывая их через пропсы на каждом уровне.
Проблема prop drilling
<App user={user}>
<Layout user={user}>
<Sidebar user={user}>
<Profile user={user} />
Layout и Sidebar не используют user, но вынуждены его передавать. Чем глубже дерево — тем хуже.
Создание контекста
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);
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>
);
}
Оборачивание приложения
function App() {
return (
<AuthProvider>
<Main />
</AuthProvider>
);
}
Использование
import { useContext } from 'react';
function Profile() {
const auth = useContext(AuthContext);
if (!auth?.user) return <p>Войдите</p>;
return <p>{auth.user.name}</p>;
}
Profile получает данные напрямую, без пропсов.
Значение по умолчанию
Если провайдера нет — вернётся значение из createContext:
const ThemeContext = createContext<'light' | 'dark'>('light');
Вложенные провайдеры
<AuthProvider>
<ThemeProvider>
<App />
</ThemeProvider>
</AuthProvider>
Внутренние компоненты видят оба контекста.
Обновление значения
Когда значение контекста меняется — все потребители перерисовываются. Это нормально, если данные действительно общие.
function ThemeProvider({ children }) {
const [theme, setTheme] = useState<'light' | 'dark'>('light');
const toggle = () => setTheme((t) => (t === 'light' ? 'dark' : 'light'));
return (
<ThemeContext.Provider value={{ theme, toggle }}>
{children}
</ThemeContext.Provider>
);
}
Кастомный хук
Оборачивай useContext в хук с проверкой:
export function useAuth() {
const context = useContext(AuthContext);
if (!context) {
throw new Error('useAuth must be used within AuthProvider');
}
return context;
}
Использование:
const { user, login, logout } = useAuth();
Итоги
- Контекст решает prop drilling
createContextсоздаёт контекст- Провайдер предоставляет значение
useContextчитает значение- Кастомный хук с проверкой — стандарт
- Обновление значения вызывает ререндер потребителей