Falytom logoFALYTOM
Games
ToolsUI ComponentsServicesProductsAbout
LoginSign Up
menu
  1. Home
  2. /Web Development
  3. /React
  4. /Usecontext Hook
Your Progress0%
0 of 42 completed

React Topics

Getting Started

  • What is React?
  • React vs Vanilla JavaScript
  • Setting Up Your Environment
  • Your First React App

JSX Fundamentals

  • Introduction to JSX
  • JSX Expressions and Variables
  • Conditional Rendering
  • Lists and Keys
  • Styling in JSX

Components Basics

  • Function Components
  • Props
  • Children Prop
  • Props Destructuring
  • Component Composition

State Management

  • Introduction to State
  • useState Hook
  • State Updates and Re-renders
  • Multiple State Variables
  • State Best Practices

Events and Interactivity

  • Handling Events
  • Event Objects
  • Forms in React
  • Form Validation

Side Effects and Data

  • Introduction to useEffect
  • Data Fetching
  • Cleanup Functions
  • Dependency Arrays

Advanced Hooks

  • useRef Hook
  • useContext Hook
  • useMemo Hook
  • useCallback Hook

React Patterns

  • Custom Hooks
  • Higher-Order Components
  • Render Props Pattern

React Router

  • Introduction to React Router
  • Routes and Navigation
  • Dynamic Routes and Parameters

Best Practices

  • Component Organization
  • Performance Optimization
  • Error Handling
  • Testing React Components

Real World Project

  • Building a Complete App

useContext Hook

Sharing state without prop drilling

Passing data through props works great for simple apps, but what happens when you need to share state between components that are many levels apart? You end up passing props through every intermediate component - this is called prop drilling and it's tedious and error-prone. The useContext hook solves this by letting you share data across your entire component tree without passing props. Let's learn how to use Context effectively! 🌳

The Problem: Prop Drilling

Imagine you have user data at the top level that needs to be accessed deep in the tree:

prop-drilling.jsx
// āŒ Problem: Prop drilling
function App() {
  const [user, setUser] = React.useState({ name: 'Alice', theme: 'dark' })
  
  return <Dashboard user={user} />
}

function Dashboard({ user }) {
  // Dashboard doesn't use user, just passes it down
  return <Sidebar user={user} />
}

function Sidebar({ user }) {
  // Sidebar doesn't use user, just passes it down
  return <Profile user={user} />
}

function Profile({ user }) {
  // Finally! Profile uses user
  return <div>Welcome, {user.name}!</div>
}

// Problems:
// 1. Dashboard and Sidebar don't need user, but must pass it
// 2. If user prop name changes, must update ALL components
// 3. Hard to maintain as app grows
// 4. Intermediate components are tightly coupled

What is Prop Drilling?

Prop drilling is when you pass data through multiple levels of components, even though intermediate components don't use the data.

It's like passing a package through many people to reach the final recipient - inefficient and annoying!

The Solution: Context

Context lets components access data without passing props:

context-solution.jsx
// āœ… Solution: Use Context
// Step 1: Create Context
const UserContext = React.createContext()

// Step 2: Provide Context
function App() {
  const [user, setUser] = React.useState({ name: 'Alice', theme: 'dark' })
  
  return (
    <UserContext.Provider value={user}>
      <Dashboard />
    </UserContext.Provider>
  )
}

// Step 3: Components in the middle don't need to know about user
function Dashboard() {
  return <Sidebar />
}

function Sidebar() {
  return <Profile />
}

// Step 4: Consume Context where needed
function Profile() {
  const user = React.useContext(UserContext)
  
  return <div>Welcome, {user.name}!</div>
}

// Benefits:
// 1. Dashboard and Sidebar are simpler
// 2. Profile directly accesses user
// 3. Adding new consumer is easy
// 4. No prop drilling!

Three Steps to Use Context

Step 1: Create Context

step1-create.jsx
// Create context outside component
const ThemeContext = React.createContext()

// Can provide default value (optional)
const ThemeContext = React.createContext('light')

// Common pattern: Create in separate file
// theme-context.js
import React from 'react'

export const ThemeContext = React.createContext('light')

// Note: Default value is used only if component has no Provider above it

Step 2: Provide Context

step2-provide.jsx
function App() {
  const [theme, setTheme] = React.useState('light')
  
  return (
    <ThemeContext.Provider value={theme}>
      {/* All children can access theme */}
      <Header />
      <Main />
      <Footer />
    </ThemeContext.Provider>
  )
}

// Provider makes value available to all descendants
// Value can be anything: string, number, object, array, function

// Passing object with state and setter:
function App() {
  const [theme, setTheme] = React.useState('light')
  
  return (
    <ThemeContext.Provider value={{ theme, setTheme }}>
      <Header />
      <Main />
      <Footer />
    </ThemeContext.Provider>
  )
}

Step 3: Consume Context

step3-consume.jsx
function Header() {
  // Use useContext hook to access value
  const theme = React.useContext(ThemeContext)
  
  return (
    <header className={theme}>
      <h1>My App</h1>
    </header>
  )
}

// If context provides object:
function ThemeToggle() {
  const { theme, setTheme } = React.useContext(ThemeContext)
  
  const toggleTheme = () => {
    setTheme(theme === 'light' ? 'dark' : 'light')
  }
  
  return (
    <button onClick={toggleTheme}>
      Current theme: {theme}
    </button>
  )
}

Complete Context Example

Theme Context (Full Implementation)

theme-context-full.jsx
// 1. Create Context
const ThemeContext = React.createContext()

// 2. Create Provider Component
function ThemeProvider({ children }) {
  const [theme, setTheme] = React.useState('light')
  
  const toggleTheme = () => {
    setTheme(prev => prev === 'light' ? 'dark' : 'light')
  }
  
  const value = {
    theme,
    toggleTheme
  }
  
  return (
    <ThemeContext.Provider value={value}>
      {children}
    </ThemeContext.Provider>
  )
}

// 3. Create Custom Hook (optional but recommended)
function useTheme() {
  const context = React.useContext(ThemeContext)
  
  if (context === undefined) {
    throw new Error('useTheme must be used within ThemeProvider')
  }
  
  return context
}

// 4. Use in App
function App() {
  return (
    <ThemeProvider>
      <Header />
      <Main />
      <Footer />
    </ThemeProvider>
  )
}

// 5. Consume in Components
function Header() {
  const { theme } = useTheme()
  
  return (
    <header className={theme}>
      <h1>My App</h1>
    </header>
  )
}

function ThemeToggle() {
  const { theme, toggleTheme } = useTheme()
  
  return (
    <button onClick={toggleTheme}>
      Switch to {theme === 'light' ? 'dark' : 'light'} mode
    </button>
  )
}

// This pattern is clean and reusable!

Multiple Contexts

You can use multiple contexts in the same app:

multiple-contexts.jsx
// Create multiple contexts
const ThemeContext = React.createContext()
const UserContext = React.createContext()
const LanguageContext = React.createContext()

function App() {
  const [theme, setTheme] = React.useState('light')
  const [user, setUser] = React.useState(null)
  const [language, setLanguage] = React.useState('en')
  
  return (
    <ThemeContext.Provider value={{ theme, setTheme }}>
      <UserContext.Provider value={{ user, setUser }}>
        <LanguageContext.Provider value={{ language, setLanguage }}>
          <Dashboard />
        </LanguageContext.Provider>
      </UserContext.Provider>
    </ThemeContext.Provider>
  )
}

// Consume multiple contexts
function Profile() {
  const { theme } = React.useContext(ThemeContext)
  const { user } = React.useContext(UserContext)
  const { language } = React.useContext(LanguageContext)
  
  return (
    <div className={theme}>
      <p>{language === 'en' ? 'Welcome' : 'Bienvenido'}, {user?.name}!</p>
    </div>
  )
}

// Better pattern: Combine related contexts
function AppProvider({ children }) {
  return (
    <ThemeProvider>
      <UserProvider>
        <LanguageProvider>
          {children}
        </LanguageProvider>
      </UserProvider>
    </ThemeProvider>
  )
}

function App() {
  return (
    <AppProvider>
      <Dashboard />
    </AppProvider>
  )
}

Interactive Demo

ReactPlayground.jsx
Code Editor
Preview

šŸ’” Tip: Edit the code above and click "Run" to see your changes

Common Context Patterns

Pattern 1: Custom Hook for Context

pattern-custom-hook.jsx
// Create context and hook together
const AuthContext = React.createContext()

function AuthProvider({ children }) {
  const [user, setUser] = React.useState(null)
  const [isLoading, setIsLoading] = React.useState(false)
  
  const login = async (credentials) => {
    setIsLoading(true)
    const user = await api.login(credentials)
    setUser(user)
    setIsLoading(false)
  }
  
  const logout = () => {
    setUser(null)
  }
  
  const value = { user, isLoading, login, logout }
  
  return (
    <AuthContext.Provider value={value}>
      {children}
    </AuthContext.Provider>
  )
}

// Custom hook with error checking
function useAuth() {
  const context = React.useContext(AuthContext)
  
  if (context === undefined) {
    throw new Error('useAuth must be used within AuthProvider')
  }
  
  return context
}

// Usage:
function LoginButton() {
  const { user, login, logout } = useAuth()
  
  if (user) {
    return <button onClick={logout}>Logout</button>
  }
  
  return <button onClick={() => login(credentials)}>Login</button>
}

// Clean API!

Pattern 2: Separate State and Actions

pattern-separate.jsx
// Separate contexts for state and actions
const TodoStateContext = React.createContext()
const TodoActionsContext = React.createContext()

function TodoProvider({ children }) {
  const [todos, setTodos] = React.useState([])
  
  const actions = {
    addTodo: (text) => {
      setTodos([...todos, { id: Date.now(), text, done: false }])
    },
    toggleTodo: (id) => {
      setTodos(todos.map(todo =>
        todo.id === id ? { ...todo, done: !todo.done } : todo
      ))
    },
    deleteTodo: (id) => {
      setTodos(todos.filter(todo => todo.id !== id))
    }
  }
  
  return (
    <TodoStateContext.Provider value={todos}>
      <TodoActionsContext.Provider value={actions}>
        {children}
      </TodoActionsContext.Provider>
    </TodoStateContext.Provider>
  )
}

// Components that only read don't re-render on actions change
function useTodos() {
  return React.useContext(TodoStateContext)
}

function useTodoActions() {
  return React.useContext(TodoActionsContext)
}

// Usage:
function TodoList() {
  const todos = useTodos()  // Only re-renders when todos change
  
  return todos.map(todo => <TodoItem key={todo.id} todo={todo} />)
}

function AddTodoForm() {
  const { addTodo } = useTodoActions()  // Doesn't re-render when todos change
  
  return <form onSubmit={e => { e.preventDefault(); addTodo(text) }}>...</form>
}

Pattern 3: Context with Reducer

pattern-reducer.jsx
const CartContext = React.createContext()

function cartReducer(state, action) {
  switch (action.type) {
    case 'ADD_ITEM':
      return [...state, action.item]
    case 'REMOVE_ITEM':
      return state.filter(item => item.id !== action.id)
    case 'CLEAR':
      return []
    default:
      return state
  }
}

function CartProvider({ children }) {
  const [cart, dispatch] = React.useReducer(cartReducer, [])
  
  const value = {
    cart,
    addItem: (item) => dispatch({ type: 'ADD_ITEM', item }),
    removeItem: (id) => dispatch({ type: 'REMOVE_ITEM', id }),
    clearCart: () => dispatch({ type: 'CLEAR' })
  }
  
  return (
    <CartContext.Provider value={value}>
      {children}
    </CartContext.Provider>
  )
}

// useReducer + Context is powerful for complex state!

Context Performance Considerations

Problem: All Consumers Re-render

performance-problem.jsx
const AppContext = React.createContext()

function AppProvider({ children }) {
  const [user, setUser] = React.useState(null)
  const [theme, setTheme] = React.useState('light')
  const [language, setLanguage] = React.useState('en')
  
  const value = { user, setUser, theme, setTheme, language, setLanguage }
  
  return (
    <AppContext.Provider value={value}>
      {children}
    </AppContext.Provider>
  )
}

function ThemeButton() {
  const { theme, setTheme } = React.useContext(AppContext)
  // Problem: Re-renders when user or language change!
  
  return <button onClick={() => setTheme('dark')}>{theme}</button>
}

// When user changes, ThemeButton re-renders even though it doesn't use user!

Solution 1: Split Contexts

performance-split.jsx
// Create separate contexts
const UserContext = React.createContext()
const ThemeContext = React.createContext()
const LanguageContext = React.createContext()

function AppProvider({ children }) {
  const [user, setUser] = React.useState(null)
  const [theme, setTheme] = React.useState('light')
  const [language, setLanguage] = React.useState('en')
  
  return (
    <UserContext.Provider value={{ user, setUser }}>
      <ThemeContext.Provider value={{ theme, setTheme }}>
        <LanguageContext.Provider value={{ language, setLanguage }}>
          {children}
        </LanguageContext.Provider>
      </ThemeContext.Provider>
    </UserContext.Provider>
  )
}

function ThemeButton() {
  const { theme, setTheme } = React.useContext(ThemeContext)
  // Only re-renders when theme changes!
  
  return <button onClick={() => setTheme('dark')}>{theme}</button>
}

Solution 2: Memoize Context Value

performance-memo.jsx
function ThemeProvider({ children }) {
  const [theme, setTheme] = React.useState('light')
  
  // āŒ Creates new object every render
  const value = { theme, setTheme }
  
  // āœ… Memoize to prevent unnecessary re-renders
  const value = React.useMemo(() => ({
    theme,
    setTheme
  }), [theme])  // Only recreate when theme changes
  
  return (
    <ThemeContext.Provider value={value}>
      {children}
    </ThemeContext.Provider>
  )
}

Context Performance Tips:

  • Split contexts by concern (don't put everything in one)
  • Memoize context values to prevent object recreation
  • Use separate contexts for state and actions
  • Don't use context for frequently changing values
  • Consider state management libraries for complex apps

When to Use Context

āœ… Good Use Cases for Context:

  • Theme/styling - Light/dark mode
  • User authentication - Current user, login state
  • Language/localization - i18n settings
  • Global settings - App configuration
  • Feature flags - A/B testing

āŒ Don't Use Context For:

  • Frequently changing data - Use state management library
  • Simple parent-child communication - Use props
  • Performance-critical updates - All consumers re-render
  • Data that only a few components need - Use props or lift state

šŸ’” Context Best Practices

• Use context for truly global data
• Create custom hooks for each context
• Provide good error messages
• Split contexts by domain
• Memoize context values
• Don't overuse - props are usually better

Key Takeaways

  • Context solves prop drilling problem
  • Three steps: Create, Provide, Consume
  • React.createContext() creates context
  • <Context.Provider> provides value to tree
  • useContext(Context) consumes value
  • Create custom hooks for better API
  • All consumers re-render when context value changes
  • Split contexts to prevent unnecessary re-renders
  • Memoize context values to prevent object recreation
  • Best for global data like theme, auth, settings
  • Don't use for frequently changing data
  • Can use multiple contexts in same component
  • Context is NOT a full state management solution

What's Next?

You now understand useContext and how to share state across your component tree! You know how to avoid prop drilling and when to use context effectively.

In the next lesson, you'll learn about useMemo Hook:

  • What is memoization and why it matters
  • Using useMemo to optimize expensive calculations
  • Preventing unnecessary re-computations
  • When to use useMemo vs when not to
  • Common memoization patterns

Get ready to optimize your React applications with memoization! ⚔

Learning how to avoid prop drilling with useContext in React!

Previous
useRef Hook
Next
useMemo Hook

Never Miss a New React Tutorial

Join 2,000+ developers learning React step-by-step. Get new tutorials, tips, and exclusive resources delivered to your inbox - completely FREE.

You might also like

UI Components

Ready-made components for your projects

Developer Tools

Boost productivity with handy generators

HTML Game

Learn HTML concepts through interactive gameplay

Join Our Web Development Training Program

Master frontend & backend development with hands-on projects.

React Tutorials

0 of 42 completed

Your Progress0%

Topics

Getting Started

  • What is React?
  • React vs Vanilla JavaScript
  • Setting Up Your Environment
  • Your First React App

JSX Fundamentals

  • Introduction to JSX
  • JSX Expressions and Variables
  • Conditional Rendering
  • Lists and Keys
  • Styling in JSX

Components Basics

  • Function Components
  • Props
  • Children Prop
  • Props Destructuring
  • Component Composition

State Management

  • Introduction to State
  • useState Hook
  • State Updates and Re-renders
  • Multiple State Variables
  • State Best Practices

Events and Interactivity

  • Handling Events
  • Event Objects
  • Forms in React
  • Form Validation

Side Effects and Data

  • Introduction to useEffect
  • Data Fetching
  • Cleanup Functions
  • Dependency Arrays

Advanced Hooks

  • useRef Hook
  • useContext Hook
  • useMemo Hook
  • useCallback Hook

React Patterns

  • Custom Hooks
  • Higher-Order Components
  • Render Props Pattern

React Router

  • Introduction to React Router
  • Routes and Navigation
  • Dynamic Routes and Parameters

Best Practices

  • Component Organization
  • Performance Optimization
  • Error Handling
  • Testing React Components

Real World Project

  • Building a Complete App
Falytom logoFALYTOM

Learn technologies like HTML, CSS, JavaScript, React, NodeJS and more through interactive games, tutorials, components and free tools. Transform your businesses with AI chatbots, SEO-optimized websites, and professional development services.

Ā© 2025 Tomilola Group
  • Facebook logo
  • Twitter logo
  • Instagram logo
  • LinkedIn logo
  • GitHub logo
  • YouTube logo