useContext Hook
useContext Hook — Managing global state in React
In React, data is typically passed from parent to child via props. However, when data needs to be accessible in many components, this method becomes inefficient. This problem is solved by the Context API, which allows creating a global state that's accessible in any component without prop drilling.
Context is React's built-in system that allows sharing data between different parts of the entire application. It consists of three main parts:
This is a classic example where we have two themes: light and dark. We want this theme to be accessible throughout the entire application without passing props to each component.
// ThemeContext.js
import React, { createContext, useState } from 'react';
export 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>
);
}
Here we created a context that provides two values: theme and toggleTheme.
// App.js
import React from 'react';
import { ThemeProvider } from './ThemeContext';
import Page from './Page';
export default function App() {
return (
<ThemeProvider>
<Page />
</ThemeProvider>
);
}
// Page.js
import React, { useContext } from 'react';
import { ThemeContext } from './ThemeContext';
export default function Page() {
const { theme, toggleTheme } = useContext(ThemeContext);
const styles = {
background: theme === 'light' ? '#fff' : '#333',
color: theme === 'light' ? '#000' : '#fff',
padding: '20px',
borderRadius: '8px',
};
return (
<div style={styles}>
<h2>Current theme: {theme}</h2>
<button onClick={toggleTheme}>Change Theme</button>
</div>
);
}
Now, when you click the button, the entire application automatically changes its theme without passing props. This approach is also very useful for user auth, language, settings, and notification systems.
React remembers which component was rendered under which Provider.
When the Provider's value changes, all components that use useContext automatically re-render.
This resembles global state management like Redux or Zustand.
You can use multiple contexts simultaneously, for example for user authentication and theme.
// UserContext.js
import React, { createContext, useState } from 'react';
export const UserContext = createContext();
export function UserProvider({ children }) {
const [user, setUser] = useState(null);
const login = (name) => setUser({ name });
const logout = () => setUser(null);
return (
<UserContext.Provider value={{ user, login, logout }}>
{children}
</UserContext.Provider>
);
}
// App.js
import { ThemeProvider } from './ThemeContext';
import { UserProvider } from './UserContext';
import HomePage from './HomePage';
export default function App() {
return (
<ThemeProvider>
<UserProvider>
<HomePage />
</UserProvider>
</ThemeProvider>
);
}
// HomePage.js
import React, { useContext } from 'react';
import { ThemeContext } from './ThemeContext';
import { UserContext } from './UserContext';
export default function HomePage() {
const { theme, toggleTheme } = useContext(ThemeContext);
const { user, login, logout } = useContext(UserContext);
return (
<div>
<h2>Theme: {theme}</h2>
<button onClick={toggleTheme}>Change Theme</button>
<hr />
{user ? (
<div>
<p>Hello, {user.name}!</p>
<button onClick={logout}>Logout</button>
</div>
) : (
<button onClick={() => login('Aram')}>Login</button>
)}
</div>
);
}
If the Provider's value creates a new object or function during every render,
all internal components will also re-render.
To avoid this, use useMemo or useCallback.
const value = useMemo(() => ({ theme, toggleTheme }), [theme]);
<ThemeContext.Provider value={value}>...
Write a component system that: