useContext Hook
Context API Recap
Context allows data to be passed through the component tree without props drilling.
import React, { createContext, useContext, useState } from 'react';
Creating a Context
const ThemeContext = createContext('light');
The Provider
A Provider wraps the components that need access to the context.
function App() {
const [theme, setTheme] = useState('light');
const toggleTheme = () => {
setTheme(prev => prev === 'light' ? 'dark' : 'light');
};
return (
<ThemeContext.Provider value={{ theme, toggleTheme }}>
<div className={theme}>
<Toolbar />
</div>
</ThemeContext.Provider>
);
}
Using useContext
function Toolbar() {
return <ThemedButton />;
}
function ThemedButton() {
const { theme, toggleTheme } = useContext(ThemeContext);
return (
<button onClick={toggleTheme} className={`btn-${theme}`}>
Theme: {theme}
</button>
);
}
Avoiding Prop Drilling
// Without context: prop drilling
function App() {
const [user, setUser] = useState(null);
return <Layout user={user} />; // Must pass the prop
}
function Layout({ user }) {
return <Sidebar user={user} />; // Must pass it again
}
function Sidebar({ user }) {
return <UserInfo user={user} />; // Must pass it again
}
// With context
const UserContext = createContext(null);
function App() {
const [user, setUser] = useState(null);
return (
<UserContext.Provider value={user}>
<Layout />
</UserContext.Provider>
);
}
// In any component
function UserInfo() {
const user = useContext(UserContext);
return <p>Hello, {user?.name}</p>;
}
Example: Theme Context (Dark/Light Mode)
const ThemeContext = createContext();
function ThemeProvider({ children }) {
const [theme, setTheme] = useState('light');
const toggleTheme = () => {
setTheme(prev => prev === 'light' ? 'dark' : 'light');
};
const styles = {
light: { bg: '#fff', color: '#000' },
dark: { bg: '#333', color: '#fff' }
};
return (
<ThemeContext.Provider value={{ theme, styles: styles[theme], toggleTheme }}>
{children}
</ThemeContext.Provider>
);
}
function App() {
return (
<ThemeProvider>
<MainContent />
</ThemeProvider>
);
}
User Auth Context
const AuthContext = createContext();
function AuthProvider({ children }) {
const [user, setUser] = useState(null);
const [token, setToken] = useState(null);
const login = async (credentials) => {
const res = await fetch('/api/login', {
method: 'POST',
body: JSON.stringify(credentials)
});
const data = await res.json();
setUser(data.user);
setToken(data.token);
};
const logout = () => {
setUser(null);
setToken(null);
};
return (
<AuthContext.Provider value={{ user, token, login, logout }}>
{children}
</AuthContext.Provider>
);
}
function Navbar() {
const { user, logout } = useContext(AuthContext);
return (
<nav>
{user ? (
<>
<span>Hello, {user.name}</span>
<button onClick={logout}>Log Out</button>
</>
) : (
<a href="/login">Log In</a>
)}
</nav>
);
}
Context is very useful for data that is shared globally. Next we will study useRef to access the DOM and store mutable values.