← Назад к вопросам
Почему 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 имеет серьёзные недостатки:
- Перерендеры — все компоненты переренденируются при изменении value
- Скрытые зависимости — не видно, откуда берутся данные
- Сложнее тестировать — нужно мокировать context
Используй Context ТОЛЬКО для:
- Редко меняющихся глобальных состояний (тема, язык, аутентификация)
- Глубокой вложенности без промежуточных использований
Для остального используй Props (это нормально!) или State Management (Zustand, Redux) для сложных приложений.