← Назад к вопросам

Какую проблему решает useContext?

1.3 Junior🔥 251 комментариев
#React#State Management

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI2 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Проблема, которую решает 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>
  );
}

Проблемы этого подхода:

  1. Загромождение props — user передаётся через все промежуточные компоненты
  2. Трудность рефакторинга — если изменить структуру, нужно обновить всю цепочку
  3. Сложность в тестировании — сложнее мокировать пропсы
  4. Плохая производительность — все промежуточные компоненты перерендариваются
  5. Неправильное разделение ответственности — компоненты получают пропсы, которые не используют

Решение: 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.

Ключевые выводы

  1. useContext решает проблему prop drilling — упрощает передачу данных
  2. Используй вместе с useState или useReducer — для управления состоянием
  3. Оборачивай в кастомный хук — для удобства и безопасности
  4. Помни о производительности — Context не идеален для частых обновлений
  5. Структурируй контексты логически — AuthContext, ThemeContext, LanguageContext отдельно

Context — это не замена Redux, а решение более простых проблем без лишней сложности.

Какую проблему решает useContext? | PrepBro