How to Use React Context in Functional Components

React Context has been a lifesaver in my development career, especially when dealing with deep component nesting.

In this tutorial, I will show you exactly how to implement React Context in functional components using the modern useContext hook.

What is React Context?

In my eight years of building React applications, the biggest headache was always “prop drilling.”

Prop drilling happens when you pass data through many layers of components that don’t even need that data.

React Context provides a way to share values like themes or user data between components without having to explicitly pass a prop through every level of the tree.

Context in Functional Components

Functional components are the standard in modern React development, and they make state management much cleaner.

Using the useContext hook allows you to subscribe to context changes with just one line of code.

I find this much more readable than the old “Consumer” wrapper components we used to use in class-based projects.

Method 1: The Basic useContext Hook Approach

This is the most common way I implement Context in my day-to-day work. It involves three simple steps: creating, providing, and consuming.

For this example, let’s imagine we are building a US Tax Bracket Calculator where the tax year needs to be accessible everywhere.

Step 1: Create the Context

First, we create a context object using React.createContext().

import React, { createContext } from 'react';

// We initialize the context with a default value
export const TaxYearContext = createContext('2024');

Step 2: Provide the Context

The Provider component wraps the part of your app that needs access to the data.

import React, { useState } from 'react';
import { TaxYearContext } from './TaxYearContext';
import TaxDashboard from './TaxDashboard';

const App = () => {
  const [taxYear, setTaxYear] = useState('2024');

  return (
    <div style={{ padding: '20px' }}>
      <h1>IRS Tax Assistant - USA</h1>
      <TaxYearContext.Provider value={taxYear}>
        <TaxDashboard />
      </TaxYearContext.Provider>
    </div>
  );
};

export default App;

Step 3: Consume the Context

Now, any functional component inside the Provider can grab the value using the useContext hook.

import React, { useContext } from 'react';
import { TaxYearContext } from './TaxYearContext';

const TaxDashboard = () => {
  // Accessing the context value directly
  const year = useContext(TaxYearContext);

  return (
    <div style={{ border: '1px solid #ccc', padding: '10px' }}>
      <h3>Current Filing Status</h3>
      <p>You are currently viewing tax regulations for the year: <strong>{year}</strong></p>
      <p>Location: United States (Federal)</p>
    </div>
  );
};

export default TaxDashboard;

You can see the output in the screenshot below.

React Context in Functional Components

Method 2: Manage Complex State with Context and useReducer

Sometimes, just passing a string isn’t enough. In professional-grade apps, I often combine Context with useReducer.

Let’s look at a High-Yield Savings Account (HYSA) dashboard example where we manage a balance and transaction history.

Here is how I set up a robust state management system for a banking feature.

import React, { createContext, useReducer, useContext } from 'react';

// 1. Define Initial State
const initialState = {
  balance: 5000.00,
  accountType: 'Premium Savings',
  bankName: 'Chase Bank - NYC Branch'
};

// 2. Define Reducer
const bankReducer = (state, action) => {
  switch (action.type) {
    case 'DEPOSIT':
      return { ...state, balance: state.balance + action.payload };
    case 'WITHDRAW':
      return { ...state, balance: state.balance - action.payload };
    default:
      return state;
  }
};

// 3. Create Context
const BankContext = createContext();

// 4. Create a Custom Provider Component
export const BankProvider = ({ children }) => {
  const [state, dispatch] = useReducer(bankReducer, initialState);

  return (
    <BankContext.Provider value={{ state, dispatch }}>
      {children}
    </BankContext.Provider>
  );
};

// 5. Build the UI Components
const AccountSummary = () => {
  const { state } = useContext(BankContext);
  
  return (
    <div className="account-card">
      <h2>{state.bankName}</h2>
      <p>Account Type: {state.accountType}</p>
      <h3>Current Balance: ${state.balance.toLocaleString('en-US')}</h3>
    </div>
  );
};

const TransactionButtons = () => {
  const { dispatch } = useContext(BankContext);

  return (
    <div>
      <button onClick={() => dispatch({ type: 'DEPOSIT', payload: 500 })}>
        Deposit $500 (Stimulus Check)
      </button>
      <button onClick={() => dispatch({ type: 'WITHDRAW', payload: 200 })}>
        Withdraw $200 (ATM)
      </button>
    </div>
  );
};

// 6. Main App Component
const BankingApp = () => {
  return (
    <BankProvider>
      <div style={{ fontFamily: 'Arial, sans-serif', maxWidth: '500px' }}>
        <AccountSummary />
        <hr />
        <TransactionButtons />
      </div>
    </BankProvider>
  );
};

export default BankingApp;

You can see the output in the screenshot below.

Context in React Functional Components

When Should You Avoid Context?

In my experience, Context is not a “silver bullet” for every state problem.

If you have data that changes every millisecond (like a mouse position or a countdown timer), Context can cause performance issues.

This happens because every component that consumes the context will re-render whenever the value changes.

For heavy lifting or global states that change frequently, I still lean toward libraries like Redux or Zustand.

Create a Custom Hook for Better Code Cleanliness

One trick I use to make my code look professional is hiding the useContext logic inside a custom hook.

This prevents you from having to import both the Context object and the useContext hook in every single file.

// Inside your Context file
export const useBank = () => {
  const context = useContext(BankContext);
  if (!context) {
    throw new Error('useBank must be used within a BankProvider');
  }
  return context;
};

// Inside your Component file
// Instead of: const { state } = useContext(BankContext);
// You simply use:
const { state } = useBank();

Performance Optimization with React.memo

When using Context, you might notice components re-rendering unnecessarily. To fix this, I often wrap my sub-components in React.memo.

This ensures that the component only re-renders if its specific props change, even if the Context provider above it updates.

Summary of Key Points

  • CreateContext: Defines the data store.
  • Provider: Wraps the components and passes the “value.”
  • useContext: The hook used inside functional components to read the data.
  • Custom Hooks: A cleaner way to export your context logic.

Implementing React Context correctly will make your codebase much easier to maintain.

It allows your team to understand the data flow without tracing props through ten different files.

I hope you found this tutorial helpful! Using these methods has significantly improved the architecture of the enterprise React apps I’ve built over the years.

In this tutorial, I’ve covered how to use React Context in functional components using different methods and real-world USA-themed examples.

You may also like to read:

51 Python Programs

51 PYTHON PROGRAMS PDF FREE

Download a FREE PDF (112 Pages) Containing 51 Useful Python Programs.

pyython developer roadmap

Aspiring to be a Python developer?

Download a FREE PDF on how to become a Python developer.

Let’s be friends

Be the first to know about sales and special discounts.