In React applications, state management is an important concept. When the process of passing props from parent to child components gets complex, which is known as props drilling, then you can use React Context API to make this process simpler.
In this post, you’ll learn:
- Why state management is crucial in React.
- What prop drilling is and its downsides.
- How to use React Context API step by step.
- Performance optimizations and best practices.
Before we get started, don’t forget to subscribe to my newsletter!
Get the latest tips, tools, and resources to level up your web development skills delivered straight to your inbox. Subscribe here!
Now let’s jump right into it!🚀
Why is State Management Important in React?
State management is important in React to keep different components in sync. In small applications, passing state from a parent component to a child component is simple. But as your app grows, you may need to share state between multiple components that are not related. This makes the process inefficient.
When state is passed at multiple levels inside a component, this is called props drilling.
For example:
const Parent = () => { const user = { name: "John" }; return <Child user={user} />; }; const Child = ({ user }) => { return <GrandChild user={user} />; }; const GrandChild = ({ user }) => { return <p>Hello, {user.name}!</p>; };
Problem:
- The user is passed down three levels, even though only GrandChild needs it.
- If more components are added, maintaining this structure becomes difficult.
Solution:
To avoid prop drilling, you can use React Context API, which provides a global store that any component can access directly. This will manage shared data efficiently.
What is React Context API?
React Context API is a built-in state management solution that provides a global store that you can access anywhere within the app and share state globally across components without passing props manually.
Why Use Context API?
Context API is useful because:
- It avoids prop drilling.
- This is a lightweight alternative to Redux.
- Needs no extra set up as it is built in React.
- Works well for small to medium-sized applications.
How to Use React Context API (Step-by-Step Guide)
Here’s a step-by-step guide to setting up context API:
Step 1: Create a Context
Create a new file UserContext.js and define a context.
import { createContext } from "react"; // Create Context export const UserContext = createContext();
Here,
- createContext() creates a new global context.
- This context will be used to share data between components.
Tip: Keep your context files in a separate folder within the app for code maintainability.
You might be wondering, “Do I need to memorize this Syntax?”
No worries! You can always refer back to this guide or the official docs whenever needed.
Step 2: Create a Context Provider
Create a context provider in the same file like this:
import { useState } from "react"; export function UserProvider({ children }) { const [user, setUser] = useState("John"); return ( <UserContext.Provider value={{ user, setUser }}> {children} </UserContext.Provider> ); }
Here,
- useState(“John”) initializes state with the value John.
- <UserContext.Provider> wraps the app and provides the user state.
- The value prop makes user and setUser available throughout the app.
Now, your UserContext.js file will look like this:
import { createContext, useState } from "react"; export const UserContext = createContext(); export function UserProvider({ children }) { const [user, setUser] = useState("John"); return ( <UserContext.Provider value={{ user, setUser }}> {children} </UserContext.Provider> ); }
Step 3: Wrap the App in the Provider
Modify App.js to wrap the app with UserProvider.
import { UserProvider } from "./UserProvider"; import Home from "./Home"; import About from "./About"; function App() { return ( <UserProvider> <Home /> <About /> </UserProvider> ); } export default App;
Here,
- The UserProvider wraps the entire application.
- Now, Home and About can access the user state without prop drilling.
Step 4: Consume the Context in Components
(A) Read Context Data using useContext Hook
import { useContext } from "react"; import { UserContext } from "./UserContext"; function Home() { // The value of user is extracted using destructuring const { user } = useContext(UserContext); return <h1>Welcome, {user}!</h1>; } export default Home;
Here,
- useContext(UserContext) gives access to the global state.
- user is directly used in the component without passing props.
(B) Updating the State in Context
import { useContext } from "react"; import { UserContext } from "./UserContext"; function About() { const { user, setUser } = useContext(UserContext); return ( <div> <h1>Current User: {user}</h1> <button onClick={() => setUser("David")}>Change User</button> </div> ); } export default About;
Here, clicking the button updates the user globally across all components.
Performance Optimization
When the context state gets updated, then all of the connected components get re-rendered, which is unnecessary. To optimize this:
Use useMemo for Context Value
Wrap the state in useMemo to prevent re-renders unless the state actually changes.
const value = useMemo(() => ({ user, setUser }), [user]);
Complete example:
export function UserProvider({ children }) { const [user, setUser] = useState("John"); const value = useMemo(() => ({ user, setUser }), [user]); return ( <UserContext.Provider value={value}> {children} </UserContext.Provider> ); }
Without useMemo, every re-render of the provider will create a new object for value={{ user, setUser }}; this will cause all the components (which are using the user state) to re-render even if the user hasn’t changed.
Split Contexts for Better Performance
Instead of using a single context for everything, create multiple contexts for different concerns (e.g., user state, theme state).
For example:
<UserProvider> <ThemeProvider> <App /> </ThemeProvider> </UserProvider>
This prevents unnecessary updates when only one state changes.
How Does This Work?
- UserProvider manages the user state (e.g., authentication, profile details).
- ThemeProvider manages theme preferences (light/dark mode).
- The App component and all its children can now access both user data and theme settings without prop drilling.
Creating Theme Context
import { createContext, useState } from "react"; export const ThemeContext = createContext(); export function ThemeProvider({ children }) { const [theme, setTheme] = useState("light"); return ( <ThemeContext.Provider value={{ theme, setTheme }}> {children} </ThemeContext.Provider> ); }
Now, any component inside <ThemeProvider> can access and update the theme.
When to Use Context API?
- When multiple components are sharing the same state.
- To avoid prop drilling.
- To avoid heavy setup of Redux.
- When you want to manage a global state with a lightweight solution.
- For managing themes, authentication, or language settings.
🎯Wrapping Up
That’s all for today!
I hope this post helps you.
If you found this post helpful, here’s how you can support my work:
☕ Buy me a coffee – Every little contribution keeps me motivated!
📩 Subscribe to my newsletter – Get the latest tech tips, tools & resources.
𝕏 Follow me on X (Twitter) – I share daily web development tips & insights.
Keep coding & happy learning!
Your vulnerability and honesty in your posts is truly admirable Thank you for being so open and authentic with your readers
This is such an important and often overlooked topic Thank you for bringing attention to it and offering valuable advice
I love how your posts are both informative and entertaining You have a talent for making even the most mundane topics interesting
It’s always a joy to stumble upon content that genuinely makes an impact and leaves you feeling inspired. Keep up the great work!
But a smiling visitant here to share the love (:, btw great design. “Reading well is one of the great pleasures that solitude can afford you.” by Harold Bloom.
Wow, I had never thought about it in that way before You have really opened my eyes to a new perspective Keep up the great work!
This is exactly what I needed to read today Your words have given me a new perspective and renewed hope Thank you
Your writing is so engaging and easy to read It makes it a pleasure to visit your blog and learn from your insights and experiences
Looking forward to your next post. Keep up the good work!
I’ve come across many blogs, but this one truly stands out in terms of quality and authenticity Keep up the amazing work!
It’s really useful. React context API is more clear now. Just have few questions on useMemo usage here. I think it’s brilliant but was thinking how it works here for objects.