The useContext hook in React provides a simple and efficient way to access values from a Context object within functional components. It eliminates the need for using a Consumer component and simplifies the process of passing data down the component tree.
When to Use useContext
- Global State Management: Sharing global data (e.g., theme, authentication status, or user preferences) across multiple components.
- Avoid Prop Drilling: When data needs to be passed through multiple levels of components,
useContextprovides a cleaner solution. - Simplify Context Consumption: Access context directly in functional components without wrapping them in
<Context.Consumer>.
Syntax
const value = useContext(MyContext);
MyContext: The context object created usingReact.createContext.value: The current context value.
Basic Example: Theme Context
Step 1: Create a Context
import React, { createContext } from "react";
export const ThemeContext = createContext();
Step 2: Provide the Context
Wrap your components with the ThemeContext.Provider and pass a value.
import React, { useState } from "react";
import { ThemeContext } from "./ThemeContext";
function App() {
const [theme, setTheme] = useState("light");
return (
<ThemeContext.Provider value={{ theme, setTheme }}>
<ChildComponent />
</ThemeContext.Provider>
);
}
export default App;
Step 3: Consume the Context Using useContext
import React, { useContext } from "react";
import { ThemeContext } from "./ThemeContext";
function ChildComponent() {
const { theme, setTheme } = useContext(ThemeContext);
return (
<div style={{
background: theme === "light"
? "#fff" : "#333", color: theme === "light" ? "#000" : "#fff"
}}>
<p>Current Theme: {theme}</p>
<button onClick={() => setTheme(theme === "light" ? "dark" : "light")}>Toggle Theme</button>
</div>
);
}
export default ChildComponent;
Another Example: Authentication Context
Step 1: Create Context
import React, { createContext } from "react";
export const AuthContext = createContext();
Step 2: Provide Authentication State
import React, { useState } from "react";
import { AuthContext } from "./AuthContext";
function App() {
const [isAuthenticated, setIsAuthenticated] = useState(false);
return (
<AuthContext.Provider value={{ isAuthenticated, setIsAuthenticated }}>
<Login />
</AuthContext.Provider>
);
}
export default App;
Step 3: Consume Authentication Context
import React, { useContext } from "react";
import { AuthContext } from "./AuthContext";
function Login() {
const { isAuthenticated, setIsAuthenticated } = useContext(AuthContext);
return (
<div>
<p>{isAuthenticated ? "Welcome, User!" : "Please Log In"}</p>
<button onClick={() => setIsAuthenticated(!isAuthenticated)}>
{isAuthenticated ? "Log Out" : "Log In"}
</button>
</div>
);
}
export default Login;
useContext with Multiple Contexts
If your application uses multiple contexts, you can combine them like this:
import React, { useContext } from "react";
import { ThemeContext } from "./ThemeContext";
import { AuthContext } from "./AuthContext";
function Dashboard() {
const { theme } = useContext(ThemeContext);
const { isAuthenticated } = useContext(AuthContext);
return (
<div style={{ background: theme === "light" ? "#fff" : "#333", color: theme === "light" ? "#000" : "#fff" }}>
{isAuthenticated ? <p>Welcome to the Dashboard</p> : <p>Please Log In</p>}
</div>
);
}
export default Dashboard;
Best Practices for useContext
- Avoid Overusing Context: Use context for truly global data. For other cases, pass props directly or use libraries like Redux/Zustand for complex state management.
- Optimize Performance:
- Minimize re-renders by memoizing context values using
React.useMemo. - Split contexts if multiple values change frequently and independently.
- Minimize re-renders by memoizing context values using
- Combine with Custom Hooks: Encapsulate
useContextlogic in a custom hook for better reusability and cleaner components.
Encapsulating useContext in a Custom Hook
import React, { createContext, useContext, useState } from "react";
const ThemeContext = createContext();
export const ThemeProvider = ({ children }) => {
const [theme, setTheme] = useState("light");
return <ThemeContext.Provider value={{ theme, setTheme }}>{children}</ThemeContext.Provider>;
};
export const useTheme = () => useContext(ThemeContext);
Usage:
import React from "react";
import { ThemeProvider, useTheme } from "./ThemeContext";
function ThemedComponent() {
const { theme, setTheme } = useTheme();
return (
<div style={{ background: theme === "light" ? "#fff" : "#333", color: theme === "light" ? "#000" : "#fff" }}>
<button onClick={() => setTheme(theme === "light" ? "dark" : "light")}>Toggle Theme</button>
</div>
);
}
function App() {
return (
<ThemeProvider>
<ThemedComponent />
</ThemeProvider>
);
}
export default App;
Key Advantages of useContext
- Simplifies the consumption of context values.
- Avoids the need for prop drilling.
- Makes code cleaner and easier to maintain.
useContext is a powerful tool for managing shared state, and when used appropriately, it can significantly enhance your application’s modularity and scalability