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

Почему Context не используется вместо атрибутов slash-props?

1.8 Middle🔥 191 комментариев
#State Management

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

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

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

Почему Context не используется вместо props (prop drilling)

Понимание проблемы: Prop Drilling

Prop drilling — это ситуация, когда нужно передавать данные через цепочку компонентов, даже если промежуточные компоненты не используют эти данные:

// ПРОБЛЕМА: Prop drilling

function App() {
  const [user, setUser] = useState({ name: "John", role: "admin" });
  return <Header user={user} />; // Передаём user
}

function Header({ user }) {
  return <Navigation user={user} />; // Передаём дальше
}

function Navigation({ user }) {
  return <UserMenu user={user} />; // Ещё передаём
}

function UserMenu({ user }) {
  return <span>{user.name}</span>; // Наконец используем!
}

// Компоненты Header и Navigation НЕ используют user,
// но вынуждены передавать его дальше

Почему Context НЕ панацея

1. Context вызывает полную перерендеризацию компонентов

// ПРОБЛЕМА С Context: излишние перерендеры

const UserContext = createContext();

function App() {
  const [user, setUser] = useState({ name: "John" });
  
  return (
    <UserContext.Provider value={user}>
      <Header />
      <ExpensiveComponent /> // Переренденируется при изменении user!
    </UserContext.Provider>
  );
}

function ExpensiveComponent() {
  // Этот компонент НЕ использует UserContext,
  // но переренденируется при каждом изменении value
  // Это BAD для производительности
  return <div>Дорогостоящие вычисления...</div>;
}

function Header() {
  const user = useContext(UserContext); // Использует context
  return <h1>Hello, {user.name}</h1>;
}

Решение для контекста:

// ✅ ПРАВИЛЬНО: разделить на несколько context провайдеров
const UserContext = createContext();

function App() {
  const [user, setUser] = useState({ name: "John" });
  
  return (
    <>
      {/* Оборачиваем только те компоненты, которые нужны для user */}
      <UserContext.Provider value={user}>
        <Header />
      </UserContext.Provider>
      <ExpensiveComponent /> {/* Теперь не переренденируется */}
    </>
  );
}

2. Props явно показывают зависимости

// С Props ЯСНО, какие данные требует компонент

function UserCard({ user, onEdit, onDelete }) {
  // Сразу видим, что нужны: user, onEdit, onDelete
  // Легко найти, где эти функции определены
  // Легко тестировать (просто передаём props)
}

// С Context эти зависимости СКРЫТЫ

function UserCard() {
  const user = useContext(UserContext);
  const { onEdit } = useContext(ActionsContext); // Откуда берётся?
  const { onDelete } = useContext(AnotherContext); // И эта?
  
  // Сложнее понять, откуда берутся данные
  // Сложнее тестировать (нужно мокировать context)
  // Можно забыть, что компонент зависит от context
}

3. Context усложняет тестирование

// ПРОСТОЕ ТЕСТИРОВАНИЕ С Props

test('UserCard displays user name', () => {
  const user = { name: "John", email: "john@example.com" };
  render(<UserCard user={user} onEdit={() => {}} onDelete={() => {}} />);
  expect(screen.getByText("John")).toBeInTheDocument();
});

// СЛОЖНОЕ ТЕСТИРОВАНИЕ С Context

test('UserCard displays user name', () => {
  const user = { name: "John", email: "john@example.com" };
  const mockContextValue = { user };
  
  render(
    <UserContext.Provider value={mockContextValue}>
      <UserCard />
    </UserContext.Provider>
  );
  
  expect(screen.getByText("John")).toBeInTheDocument();
});

// Нужно создавать провайдер, мокировать контекст,
// больше кода для простого теста

Когда ПРАВИЛЬНО использовать Context

// ✅ ПРАВИЛЬНОЕ ИСПОЛЬЗОВАНИЕ Context

// 1. Глобальное состояние, которое нужно РЕДКО менять
const ThemeContext = createContext();

function App() {
  const [theme, setTheme] = useState('light');
  
  // theme меняется редко, поэтому мало перерендеров
  return (
    <ThemeContext.Provider value={{ theme, setTheme }}>
      <MainContent />
      <Sidebar />
      <Footer />
    </ThemeContext.Provider>
  );
}

// 2. Аутентификация (меняется редко, нужна везде)
const AuthContext = createContext();

function App() {
  const [user, setUser] = useState(null);
  
  return (
    <AuthContext.Provider value={{ user, login, logout }}>
      <Router />
    </AuthContext.Provider>
  );
}

// 3. Многоуровневая вложенность без промежуточных компонентов
function App() {
  const [settings, setSettings] = useState({});
  
  return (
    <SettingsContext.Provider value={settings}>
      <Dashboard>
        <Section>
          <Component> {/* Component нужны settings */}
            <DeepComponent /> {/* И этому тоже нужны */}
          </Component>
        </Section>
      </Dashboard>
    </SettingsContext.Provider>
  );
}

Когда НЕПРАВИЛЬНО использовать Context

// ❌ НЕПРАВИЛЬНОЕ ИСПОЛЬЗОВАНИЕ

// 1. State, который часто меняется
const FormContext = createContext();

function App() {
  const [formData, setFormData] = useState({ /* 10 полей */ });
  
  return (
    <FormContext.Provider value={{ formData, setFormData }}>
      <Form /> {/* При КАЖДОМ изменении поля переренденируются все компоненты */}
    </FormContext.Provider>
  );
}

// ✅ ЛУЧШЕ использовать локальное состояние или state management
function Form() {
  const [formData, setFormData] = useState({});
  return <Fields formData={formData} onChange={setFormData} />;
}

// 2. Props, которые проходят через 1-2 компонента
// Это просто prop drilling, но Context здесь излишний

function App() {
  const [user, setUser] = useState({});
  return <UserProfile user={user} />; // Просто передай!
}

function UserProfile({ user }) {
  return <UserCard user={user} />; // И сюда тоже
}

function UserCard({ user }) {
  return <p>{user.name}</p>;
}

Props + State Management вместо Context

// РЕКОМЕНДУЕМЫЙ ПОДХОД: Использовать Redux, Zustand или простое state management

// Пример с Zustand (простой и эффективный)
import create from 'zustand';

const useUserStore = create((set) => ({
  user: null,
  setUser: (user) => set({ user }),
  logout: () => set({ user: null }),
}));

function App() {
  const user = useUserStore((state) => state.user);
  const setUser = useUserStore((state) => state.setUser);
  
  // Zustand обновляет ТОЛЬКО компоненты, которые используют этот state
  // Нет излишних перерендеров!
  return (
    <>
      <Header user={user} />
      <MainContent />
    </>
  );
}

function Header({ user }) {
  return <h1>Hello, {user?.name}</h1>;
}

Сравнение: Props vs Context vs State Management

// PROPS
// Плюсы: ясные зависимости, легко тестировать
// Минусы: prop drilling при глубокой вложенности
// Когда использовать: большинство случаев (1-3 уровня вложенности)

// CONTEXT
// Плюсы: избегаем prop drilling
// Минусы: излишние перерендеры, скрытые зависимости
// Когда использовать: редко меняющиеся глобальные состояния

// STATE MANAGEMENT (Redux, Zustand, Recoil)
// Плюсы: производительность, явная логика обновлений, отладка
// Минусы: больше кода, кривая обучения
// Когда использовать: сложное приложение с много state

Итог

Props — это не плохо! Context имеет серьёзные недостатки:

  1. Перерендеры — все компоненты переренденируются при изменении value
  2. Скрытые зависимости — не видно, откуда берутся данные
  3. Сложнее тестировать — нужно мокировать context

Используй Context ТОЛЬКО для:

  • Редко меняющихся глобальных состояний (тема, язык, аутентификация)
  • Глубокой вложенности без промежуточных использований

Для остального используй Props (это нормально!) или State Management (Zustand, Redux) для сложных приложений.