Какую проблему решает useContext?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Проблема, которую решает useContext
useContext — это React хук, который решает фундаментальную проблему в приложениях: передача данных между компонентами, которые находятся далеко друг от друга в иерархии компонентов. Эта проблема называется "prop drilling" (пробуривание props).
Проблема: Prop Drilling
Представьте классическую ситуацию:
function App() {
const user = { name: "Алиса", id: 123 };
return (
<Layout user={user}>
<MainPage user={user}>
<Section user={user}>
<Card user={user}>
<UserProfile user={user} />
</Card>
</Section>
</MainPage>
</Layout>
);
}
Проблемы этого подхода:
- Загромождение props —
userпередаётся через все промежуточные компоненты - Трудность рефакторинга — если изменить структуру, нужно обновить всю цепочку
- Сложность в тестировании — сложнее мокировать пропсы
- Плохая производительность — все промежуточные компоненты перерендариваются
- Неправильное разделение ответственности — компоненты получают пропсы, которые не используют
Решение: useContext
Context позволяет создать глобальное хранилище для определённой части дерева компонентов, и любой компонент может получить значение из этого хранилища напрямую, минуя промежуточные компоненты.
Как это работает
import { createContext, useContext } from 'react';
const UserContext = createContext();
export function UserProvider({ children, user }) {
return (
<UserContext.Provider value={user}>
{children}
</UserContext.Provider>
);
}
export function useUser() {
const user = useContext(UserContext);
if (!user) {
throw new Error('useUser должен быть внутри UserProvider');
}
return user;
}
Использование в компонентах
function App() {
const user = { name: "Алиса", id: 123 };
return (
<UserProvider user={user}>
<Layout>
<MainPage>
<Section>
<Card>
<UserProfile />
</Card>
</Section>
</MainPage>
</Layout>
</UserProvider>
);
}
function UserProfile() {
const user = useUser();
return <div>Привет, {user.name}!</div>;
}
Обратите внимание: компоненты Layout, MainPage, Section, Card больше не нужно передавать пропс user.
Практические примеры использования
1. Аутентификация
const AuthContext = createContext();
export function AuthProvider({ children }) {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
checkAuth().then(userData => {
setUser(userData);
setLoading(false);
});
}, []);
const login = async (email, password) => {
const userData = await loginUser(email, password);
setUser(userData);
};
const logout = () => setUser(null);
return (
<AuthContext.Provider value={{ user, loading, login, logout }}>
{children}
</AuthContext.Provider>
);
}
export function useAuth() {
return useContext(AuthContext);
}
2. Тема оформления
const ThemeContext = createContext();
export 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>
);
}
export function useTheme() {
return useContext(ThemeContext);
}
function Header() {
const { theme, toggleTheme } = useTheme();
return (
<header className={theme === 'dark' ? 'dark' : 'light'}>
<button onClick={toggleTheme}>Изменить тему</button>
</header>
);
}
3. Локализация
const LanguageContext = createContext();
export function LanguageProvider({ children }) {
const [language, setLanguage] = useState('ru');
const t = (key) => {
const translations = {
'ru': { greeting: 'Привет' },
'en': { greeting: 'Hello' }
};
return translations[language][key];
};
return (
<LanguageContext.Provider value={{ language, setLanguage, t }}>
{children}
</LanguageContext.Provider>
);
}
Когда использовать useContext
Используй Context когда: данные нужны многим компонентам на разных уровнях иерархии, данные меняются редко (тема, язык, аутентификация), нужно избежать prop drilling.
Не используй Context когда: данные часто меняются (тысячи обновлений в секунду), нужна высокая производительность, данные используются только в одном-двух компонентах.
Производительность: Context vs Redux
Проблема с Context:
const UserContext = createContext();
function App() {
const [user, setUser] = useState({ name: 'Алиса' });
return (
<UserContext.Provider value={{ user, setUser }}>
{children}
</UserContext.Provider>
);
}
Все компоненты, подписанные на этот Context, будут перерендариваться при каждом изменении user.
Решение:
const value = useMemo(() => ({ user, setUser }), [user]);
return (
<UserContext.Provider value={value}>
{children}
</UserContext.Provider>
);
Для очень частых обновлений или большого количества данных используйте Redux или Zustand.
Ключевые выводы
- useContext решает проблему prop drilling — упрощает передачу данных
- Используй вместе с useState или useReducer — для управления состоянием
- Оборачивай в кастомный хук — для удобства и безопасности
- Помни о производительности — Context не идеален для частых обновлений
- Структурируй контексты логически — AuthContext, ThemeContext, LanguageContext отдельно
Context — это не замена Redux, а решение более простых проблем без лишней сложности.