Sharing State: React's `useContext` Hook

Escape the tangled web of "prop drilling" and learn to share state cleanly across your entire application.

Lesson ProgressStep 1 of 10
App (user)
⬇️
Page (user)
⬇️
Header (user)
⬇️
Greeting (user)
App (Provider)
⬇️
Page
⬇️
Header
⬇️
Greeting (useContext)
0 EXP

Hello! Let's solve one of React's biggest challenges: sharing data between components.

// The Prop Drilling Problem

The Problem: Prop Drilling

In a React application, state is often held by top-level components. To get this state to a deeply nested component, you have to pass it down as props through every intermediate component.

This is called "prop drilling".

function App() {
  const user = { name: "Alice" };
  return <Layout user={user} />;
}
function Layout({ user }) {
  // Layout doesn't need 'user', but must pass it.
  return <Header user={user} />;
}
function Header({ user }) {
  return <UserGreeting user={user} />;
}
function UserGreeting({ user }) {
  return <h1>Hello, {user.name}</h1>;
}

This is problematic because it makes components less reusable, harder to refactor, and adds "noise" to components that don't care about the data.

System Check

What is the primary disadvantage of prop drilling?

Advanced Holo-Simulations

0 EXP

Log in to unlock these advanced training modules and test your skills.


Achievements

🏆
Context Creator

Create a Context object and wrap components in its Provider.

🏗️
Context Consumer

Correctly use the `useContext` hook to consume a value.

✍️
Prop Drilling Slayer

Demonstrate understanding of the Context flow.

Mission: Build a Themed Button

Fix the code. Create a `ThemeContext`, `Provide` the "dark" value from `App`, and `useContext` in `ThemedButton` to style the button.

A.D.A. Feedback:

> System integrity looks stable. Code is valid.

Challenge: Order the Context Flow

Drag the steps into the correct logical order from top to bottom.

<MyContext.Provider value={...}>
const value = useContext(MyContext)
const MyContext = createContext()

Challenge: Complete the Syntax

Fill in the missing parts to complete the Context flow.

const UserContext =(null);
<UserContext.Provider={user}>...
const user =(UserContext);

Consult A.D.A.

Community Holo-Net

Peer Project Review

Submit your "Themed Button" project for feedback from other Net-Runners.

React Context: From Basics to Advanced Patterns

The React Context API provides a way to pass data through the component tree without having to pass props down manually at every level. It's React's built-in solution for state management that shines for certain types of "global" data.

The "Prop Drilling" Nightmare

Imagine a top-level component that fetches user data. Now, a deeply nested navigation bar needs to display the user's name. To get the data there, you'd have to pass the `user` prop through every single intermediate component, even if they don't use it.

// App.js
function App() {
  const [user, setUser] = useState(null);
  // ... fetch user
  return <Layout user={user} />;
}

// Layout.js
function Layout({ user }) {
  // Layout doesn't care about 'user'
  return <Header user={user} />;
}

// Header.js
function Header({ user }) {
  // Header doesn't care about 'user'
  return <UserGreeting user={user} />;
}

// UserGreeting.js
function UserGreeting({ user }) {
  return <span>Hello, {user.name}</span>;
}

This is **prop drilling**. It's brittle (what if a component in the middle changes?), tedious, and makes components harder to reuse.

The Context API Solution (The 3 Steps)

Context solves this by creating a global "channel" for your data.

  • 1. Create the Context:

    You call `createContext()` outside your components. The value you pass is the **default value**, used only if a component tries to consume the context without a Provider.

    const UserContext = React.createContext(null);
  • 2. Provide the Value:

    Wrap a parent component (like `App`) with the context's `Provider` component. It takes a `value` prop.

    <UserContext.Provider value={user}>
      <Layout />
    </UserContext.Provider>
  • 3. Consume the Value:

    Any child component, no matter how deep, can now read the value using the `useContext` hook.

    // UserGreeting.js
    const user = useContext(UserContext);
    return <span>Hello, {user.name}</span>;

Notice `Layout` and `Header` are now clean—they don't need to know `user` exists.

Performance Deep Dive: The Gotchas

Context is powerful, but it has a major performance trap. **When a Context Provider's `value` prop changes, all components that consume that context will re-render.**

This is fine, but the trap is when you pass a new object or array to the `value` prop on every render:

❌ Bad Practice

function App() {
  const [user, setUser] = useState(...);
  // This object is new on EVERY render
  return (
    <UserContext.Provider value={{ user, setUser }}>
      <RestOfApp />
    </UserContext.Provider>
  );
}

This causes all consumers of `UserContext` to re-render, even if `user` and `setUser` haven't changed!

✔️ Good Practice

function App() {
  const [user, setUser] = useState(...);
  
  // Memoize the value
  const providerValue = useMemo(() => (
    { user, setUser }
  ), [user, setUser]);

  return (
    <UserContext.Provider value={providerValue}>
      <RestOfApp />
    </UserContext.Provider>
  );
}

`useMemo` ensures the `providerValue` object is only created when `user` or `setUser` actually changes.

An even better pattern is to **split contexts**. If one part of your state changes often (like state) and one doesn't (like the setter function), put them in separate contexts.

const UserStateContext = createContext();
const UserDispatchContext = createContext();

// Provider
<UserStateContext.Provider value={user}>
  <UserDispatchContext.Provider value={setUser}>
    <RestOfApp />
  </UserDispatchContext.Provider>
</UserStateContext.Provider>

// Component that only needs to *read* state:
const user = useContext(UserStateContext);

// Component that only needs to *update* state:
const setUser = useContext(UserDispatchContext);

Now, a component that only *updates* state won't re-render when the `user` state itself changes.

Key Takeaway: Use Context for **low-frequency, global state** like authentication, theme, or user settings. For high-frequency state (like form inputs) or complex data flows, stick to local state or consider a dedicated state-management library.

React Context API Glossary

Prop Drilling
The process of passing props down through multiple levels of nested components, even if the intermediate components do not need the props. Context is the primary solution for this.
`React.createContext(defaultValue)`
A function that creates a Context object. This object contains the `Provider` and `Consumer` components.
`defaultValue`
The value passed to `createContext()`. This value is only used when a component consumes the context but has no matching `Provider` above it in the tree.
`<MyContext.Provider value={...}>`
A component that "broadcasts" a value to all its descendants. It accepts a `value` prop. Any component consuming this context will re-render when this `value` prop changes.
`useContext(MyContext)`
A React hook that lets a functional component "listen" to a context. It finds the nearest `Provider` ancestor and returns its `value` prop.
`<MyContext.Consumer>` (Legacy)
The older, pre-hooks way to consume a context. It uses a "render prop" pattern: `<MyContext.Consumer>{value => ( /* JSX */ )}</MyContext.Consumer>`. You will still see this in class components.
State Management
The practice of managing and sharing data (state) across an application. Context is a form of built-in state management, often compared to libraries like Redux or Zustand.

About the Author

Author's Avatar

TodoTutorial Team

Passionate developers and educators making programming accessible to everyone.

This article was written and reviewed by our team of web development experts, who have years of experience teaching React and building robust, scalable web applications.

Verification and Updates

Last reviewed: October 2025.

We strive to keep our content accurate and up-to-date. This tutorial is based on the latest React (v18+) specifications and is periodically reviewed to reflect industry best practices.

External Resources

Found an error or have a suggestion? Contact us!