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,
useContext
provides 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
useContext
logic 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