useContext is a React hook that allows you to access values from React Context It provides a way to pass data deep into the component tree without prop drilling.
Key features:
React.createContext()
Usage example:
import React, { createContext, useContext } from 'react';
const ThemeContext = createContext('light');
function App() {
return (
<ThemeContext.Provider value="dark">
<Toolbar />
</ThemeContext.Provider>
);
}
function Toolbar() {
return (
<div>
<ThemedButton />
</div>
);
}
function ThemedButton() {
const theme = useContext(ThemeContext);
return (
<button className={theme}>
Button with theme {theme}
</button>
);
}
The useContext
hook is one of React’s built-in hooks that provides a way to subscribe to context and get its values in functional components. It’s a modern alternative to the old Consumer API, making code more readable and concise. 🌟
useContext
takes a context object (the value returned from React.createContext
) and returns the current context value for that context:
const value = useContext(MyContext);
A component calling useContext
will always re-render when the context value changes. React takes the current context value from the nearest matching Provider above the component in the tree.
import React, { createContext, useContext } from 'react';
// 1. Create context with initial value
const UserContext = createContext({
name: 'Guest',
isLoggedIn: false
});
// 2. Provider component
function UserProvider({ children }) {
const [user, setUser] = React.useState({
name: 'Alexander',
isLoggedIn: true
});
return (
<UserContext.Provider value={user}>
{children}
</UserContext.Provider>
);
}
// 3. Component using context
function UserProfile() {
const user = useContext(UserContext);
return (
<div>
<h1>Hello, {user.name}!</h1>
<p>Status: {user.isLoggedIn ? 'Logged in' : 'Guest'}</p>
</div>
);
}
// 4. Usage in application
function App() {
return (
<UserProvider>
<UserProfile />
</UserProvider>
);
}
import React, { createContext, useContext, useState } from 'react';
const ThemeContext = createContext();
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>
);
}
function ThemedButton() {
const { theme, toggleTheme } = useContext(ThemeContext);
return (
<button
className={`btn btn-${theme}`}
onClick={toggleTheme}
>
Switch to {theme === 'light' ? 'dark' : 'light'} theme
</button>
);
}
function App() {
return (
<ThemeProvider>
<div className="app">
<h1>App with theming</h1>
<ThemedButton />
</div>
</ThemeProvider>
);
}
import React, { createContext, useContext, useReducer } from 'react';
const AuthContext = createContext();
const authReducer = (state, action) => {
switch (action.type) {
case 'LOGIN':
return {
user: action.payload,
isAuthenticated: true
};
case 'LOGOUT':
return {
user: null,
isAuthenticated: false
};
default:
return state;
}
};
function AuthProvider({ children }) {
const [state, dispatch] = useReducer(authReducer, {
user: null,
isAuthenticated: false
});
const login = (userData) => {
dispatch({ type: 'LOGIN', payload: userData });
};
const logout = () => {
dispatch({ type: 'LOGOUT' });
};
return (
<AuthContext.Provider value={{ ...state, login, logout }}>
{children}
</AuthContext.Provider>
);
}
function UserProfile() {
const { user, isAuthenticated, logout } = useContext(AuthContext);
if (!isAuthenticated) {
return <div>Please log in</div>;
}
return (
<div>
<h2>Hello, {user.name}!</h2>
<button onClick={logout}>Log out</button>
</div>
);
}
import React, { createContext, useContext } from 'react';
const translations = {
en: {
greeting: 'Hello',
welcome: 'Welcome to our app'
},
ru: {
greeting: 'Привет',
welcome: 'Добро пожаловать в наше приложение'
}
};
const LanguageContext = createContext();
function LanguageProvider({ children, defaultLanguage = 'en' }) {
const [language, setLanguage] = React.useState(defaultLanguage);
const t = (key) => {
return translations[language]?.[key] || key;
};
return (
<LanguageContext.Provider value={{ language, setLanguage, t }}>
{children}
</LanguageContext.Provider>
);
}
function Greeting() {
const { t, language, setLanguage } = useContext(LanguageContext);
return (
<div>
<h1>{t('greeting')}!</h1>
<p>{t('welcome')}</p>
<select
value={language}
onChange={(e) => setLanguage(e.target.value)}
>
<option value="en">English</option>
<option value="ru">Русский</option>
</select>
</div>
);
}
// ❌ Without context - prop drilling
function App() {
const theme = 'dark';
return <Layout theme={theme} />;
}
function Layout({ theme }) {
return <Header theme={theme} />;
}
function Header({ theme }) {
return <Button theme={theme} />;
}
function Button({ theme }) {
return <button className={`btn-${theme}`}>Button</button>;
}
// ✅ With context - direct access
const ThemeContext = createContext('light');
function App() {
return (
<ThemeContext.Provider value="dark">
<Layout />
</ThemeContext.Provider>
);
}
function Layout() {
return <Header />;
}
function Header() {
return <Button />;
}
function Button() {
const theme = useContext(ThemeContext);
return <button className={`btn-${theme}`}>Button</button>;
}
// ❌ Complex API with many props
function ComplexComponent({
theme,
locale,
permissions,
userSettings,
// ... 10 more props
}) {
// ...
}
// ✅ Clean API with context
function CleanComponent() {
// Get all needed data from contexts
const theme = useContext(ThemeContext);
const locale = useContext(LocaleContext);
const permissions = useContext(PermissionsContext);
const userSettings = useContext(UserSettingsContext);
// ...
}
✅ Use useContext when:
// Good useContext application
const AppSettingsContext = createContext();
function App() {
return (
<AppSettingsContext.Provider value={{
theme: 'dark',
language: 'en',
notifications: true
}}>
<MainLayout />
</AppSettingsContext.Provider>
);
}
function NotificationPanel() {
const { notifications } = useContext(AppSettingsContext);
if (!notifications) return null;
return <div>Notification panel</div>;
}
❌ Avoid useContext when:
// Bad useContext application
const ExpensiveDataContext = createContext();
// ❌ Context updates too frequently
function DataProvider({ children }) {
const [data, setData] = useState([]);
// Updating data every second
useEffect(() => {
const interval = setInterval(() => {
setData(prev => [...prev, Date.now()]);
}, 1000);
return () => clearInterval(interval);
}, []);
return (
<ExpensiveDataContext.Provider value={data}>
{children}
</ExpensiveDataContext.Provider>
);
}
// All components will re-render every second
function ExpensiveComponent() {
const data = useContext(ExpensiveDataContext);
// Expensive operation with data
return <div>{JSON.stringify(data)}</div>;
}
// ❌ One large context
const AppContext = createContext();
function AppProvider({ children }) {
const [theme, setTheme] = useState('light');
const [user, setUser] = useState(null);
const [notifications, setNotifications] = useState([]);
return (
<AppContext.Provider value={{
theme, setTheme,
user, setUser,
notifications, setNotifications
}}>
{children}
</AppContext.Provider>
);
}
// ✅ Split contexts
const ThemeContext = createContext();
const UserContext = createContext();
const NotificationsContext = createContext();
function ThemeProvider({ children }) {
const [theme, setTheme] = useState('light');
return (
<ThemeContext.Provider value={{ theme, setTheme }}>
{children}
</ThemeContext.Provider>
);
}
function UserProvider({ children }) {
const [user, setUser] = useState(null);
return (
<UserContext.Provider value={{ user, setUser }}>
{children}
</UserContext.Provider>
);
}
// Components re-render only when their data changes
function ThemeToggle() {
const { theme, setTheme } = useContext(ThemeContext);
// Re-renders only when theme changes
}
function UserProfile() {
const { user } = useContext(UserContext);
// Re-renders only when user changes
}
function ThemeProvider({ children }) {
const [theme, setTheme] = useState('light');
const [accentColor, setAccentColor] = useState('blue');
// ❌ New object value on every render
const contextValue = {
theme,
setTheme,
accentColor,
setAccentColor
};
// ✅ Memoizing context value
const contextValue = React.useMemo(() => ({
theme,
setTheme,
accentColor,
setAccentColor
}), [theme, setTheme, accentColor, setAccentColor]);
return (
<ThemeContext.Provider value={contextValue}>
{children}
</ThemeContext.Provider>
);
}
// Custom hook for working with context
function useTheme() {
const context = useContext(ThemeContext);
if (!context) {
throw new Error('useTheme must be used within ThemeProvider');
}
return context;
}
// Usage
function ThemedButton() {
const { theme, toggleTheme } = useTheme();
return (
<button
className={`btn-${theme}`}
onClick={toggleTheme}
>
Toggle theme
</button>
);
}
// ❌ Error: using context without provider
const ThemeContext = createContext('light');
function Button() {
const theme = useContext(ThemeContext); // Will be 'light' (default value)
return <button className={theme}>Button</button>;
}
// ✅ Correctly: using with provider
function App() {
return (
<ThemeContext.Provider value="dark">
<Button />
</ThemeContext.Provider>
);
}
// ❌ Error: directly mutating context object
const UserContext = createContext();
function UserProvider({ children }) {
const [user, setUser] = useState({ name: 'John', age: 30 });
// Incorrect object mutation
const updateUser = (field, value) => {
user[field] = value; // Mutates state directly
setUser(user); // Won't trigger re-render
};
return (
<UserContext.Provider value={{ user, updateUser }}>
{children}
</UserContext.Provider>
);
}
// ✅ Correctly: creating new object
function UserProvider({ children }) {
const [user, setUser] = useState({ name: 'John', age: 30 });
const updateUser = (field, value) => {
setUser(prev => ({ ...prev, [field]: value })); // Creates new object
};
return (
<UserContext.Provider value={{ user, updateUser }}>
{children}
</UserContext.Provider>
);
}
// ❌ Error: creating context inside component
function App() {
// Context is created on every render
const ThemeContext = createContext('light');
return (
<ThemeContext.Provider value="dark">
<Button />
</ThemeContext.Provider>
);
}
// ✅ Correctly: creating context at top level
const ThemeContext = createContext('light');
function App() {
return (
<ThemeContext.Provider value="dark">
<Button />
</ThemeContext.Provider>
);
}
// ❌ Error: using hook in condition
function Component() {
if (someCondition) {
const value = useContext(MyContext); // Hook rules error!
// ...
}
// ...
}
// ✅ Correctly: always call hooks at top level
function Component() {
const value = useContext(MyContext); // Correctly
if (someCondition) {
// Use value inside condition
// ...
}
// ...
}
Criterion | useContext | Redux |
---|---|---|
Complexity | Simple | Complex |
Bundle size | 0 (built-in) | ~2KB |
Debugging | Limited | Extended (Redux DevTools) |
Scalability | Limited | High |
Learning curve | Low | High |
Suitable for | Small applications | Complex applications |
// useContext for simple application
const AppStateContext = createContext();
function AppStateProvider({ children }) {
const [state, setState] = useState({
user: null,
theme: 'light',
notifications: []
});
return (
<AppStateContext.Provider value={[state, setState]}>
{children}
</AppStateContext.Provider>
);
}
// Redux for complex application
import { createStore } from 'redux';
import { useSelector, useDispatch } from 'react-redux';
const store = createStore(rootReducer);
function UserProfile() {
const user = useSelector(state => state.user);
const dispatch = useDispatch();
// ...
}
Criterion | useContext | Props |
---|---|---|
Readability | High (less code) | Medium (many props) |
Explicitness | Low (dependencies hidden) | High (everything passed explicitly) |
Flexibility | Limited | High |
Performance | Medium | High |
Maintainability | Medium | High |
// Props - explicit data passing
function Parent() {
const theme = 'dark';
return <Child theme={theme} />;
}
function Child({ theme }) {
return <GrandChild theme={theme} />;
}
function GrandChild({ theme }) {
return <button className={theme}>Button</button>;
}
// useContext - implicit data passing
const ThemeContext = createContext('light');
function Parent() {
return (
<ThemeContext.Provider value="dark">
<Child />
</ThemeContext.Provider>
);
}
function Child() {
return <GrandChild />;
}
function GrandChild() {
const theme = useContext(ThemeContext);
return <button className={theme}>Button</button>;
}
✅ useContext is a React hook for:
✅ When to use:
❌ When to avoid:
✅ Best practices:
useMemo
to memoize context valuesuseContext is a powerful tool for state management in React applications, helping to avoid prop drilling and make code more readable. However, it’s important to understand when to use it and when to prefer more specialized solutions. 🚀
Want more articles to prepare for interviews? Subscribe to EasyAdvice, bookmark the site and improve yourself every day 💪