Falytom logoFALYTOM
Games
ToolsUI ComponentsServicesProductsAbout
LoginSign Up
menu
  1. Home
  2. /Web Development
  3. /React
  4. /Higher Order Components
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

Higher-Order Components

Enhancing components with the HOC pattern

Before hooks existed, React developers used Higher-Order Components (HOCs) to share logic between components. An HOC is a function that takes a component and returns a new component with additional props or behavior. While custom hooks are now preferred for most use cases, HOCs are still used in many codebases and libraries (like Redux's connect and React Router's withRouter). Let's learn this classic React pattern! šŸŽ­

What Are Higher-Order Components?

An HOC is a function that takes a component and returns a new enhanced component:

hoc-basic.jsx
// Basic HOC structure
function withSomething(Component) {
  return function EnhancedComponent(props) {
    // Add extra functionality here
    const extraProp = 'some value'
    
    // Return original component with extra props
    return <Component {...props} extraProp={extraProp} />
  }
}

// Original component
function MyComponent({ name, extraProp }) {
  return (
    <div>
      <p>Name: {name}</p>
      <p>Extra: {extraProp}</p>
    </div>
  )
}

// Enhanced component
const EnhancedMyComponent = withSomething(MyComponent)

// Usage
function App() {
  return <EnhancedMyComponent name="Alice" />
}

// MyComponent receives both 'name' and 'extraProp'!

HOC Definition:

A Higher-Order Component is:
• A function (not a component)
• Takes a component as input
• Returns a new component as output
• The new component wraps the original
• Adds props, behavior, or both

Pattern: HOC(Component) → EnhancedComponent

Why Use HOCs?

HOCs solve the problem of code reuse without components knowing about each other:

The Problem: Repeated Logic

problem-repeated.jsx
// āŒ Component 1: Repeated loading logic
function UserProfile() {
  const [user, setUser] = React.useState(null)
  const [loading, setLoading] = React.useState(true)
  
  React.useEffect(() => {
    fetch('/api/user')
      .then(res => res.json())
      .then(data => {
        setUser(data)
        setLoading(false)
      })
  }, [])
  
  if (loading) return <div>Loading...</div>
  return <div>{user.name}</div>
}

// āŒ Component 2: Same loading logic!
function PostList() {
  const [posts, setPosts] = React.useState([])
  const [loading, setLoading] = React.useState(true)
  
  React.useEffect(() => {
    fetch('/api/posts')
      .then(res => res.json())
      .then(data => {
        setPosts(data)
        setLoading(false)
      })
  }, [])
  
  if (loading) return <div>Loading...</div>
  return <ul>{posts.map(p => <li key={p.id}>{p.title}</li>)}</ul>
}

// Problem: Loading logic duplicated!

The Solution: Extract to HOC

solution-hoc.jsx
// āœ… HOC that adds loading logic
function withLoading(Component, fetchData) {
  return function WithLoadingComponent(props) {
    const [data, setData] = React.useState(null)
    const [loading, setLoading] = React.useState(true)
    
    React.useEffect(() => {
      fetchData()
        .then(result => {
          setData(result)
          setLoading(false)
        })
    }, [])
    
    if (loading) return <div>Loading...</div>
    
    return <Component {...props} data={data} />
  }
}

// āœ… Simple components that receive data
function UserProfile({ data }) {
  return <div>{data.name}</div>
}

function PostList({ data }) {
  return <ul>{data.map(p => <li key={p.id}>{p.title}</li>)}</ul>
}

// āœ… Enhance components with HOC
const UserProfileWithLoading = withLoading(
  UserProfile,
  () => fetch('/api/user').then(res => res.json())
)

const PostListWithLoading = withLoading(
  PostList,
  () => fetch('/api/posts').then(res => res.json())
)

// Loading logic written once, used many times!

HOC Conventions

1. Naming: Start with "with"

convention-naming.jsx
// āœ… Good - Clear HOC names
withAuth
withLoading
withLogger
withRouter
withTheme

// āŒ Bad - Unclear names
enhance
wrap
modify

2. Pass Through Props

convention-props.jsx
// āœ… Good - Pass through all props
function withExtraData(Component) {
  return function WithExtraData(props) {
    const extraData = 'some data'
    
    // Spread all original props + add new ones
    return <Component {...props} extraData={extraData} />
  }
}

// āŒ Bad - Props not passed through
function withExtraData(Component) {
  return function WithExtraData(props) {
    const extraData = 'some data'
    
    // Original props lost!
    return <Component extraData={extraData} />
  }
}

3. Don't Mutate Component

convention-mutate.jsx
// āŒ Bad - Mutating original component
function withLogger(Component) {
  Component.prototype.componentDidUpdate = function() {
    console.log('Updated')
  }
  return Component  // Mutation!
}

// āœ… Good - Return new component
function withLogger(Component) {
  return function WithLogger(props) {
    React.useEffect(() => {
      console.log('Rendered with props:', props)
    })
    
    return <Component {...props} />
  }
}

4. Set Display Name

convention-displayname.jsx
function withAuth(Component) {
  const WithAuth = (props) => {
    // ... auth logic
    return <Component {...props} />
  }
  
  // āœ… Set displayName for debugging
  WithAuth.displayName = `WithAuth(${Component.displayName || Component.name || 'Component'})`
  
  return WithAuth
}

// Shows "WithAuth(UserProfile)" in React DevTools

Common HOC Patterns

1. withAuth - Authentication Check

pattern-auth.jsx
function withAuth(Component) {
  return function WithAuth(props) {
    const { user, loading } = useAuth()  // Custom hook
    
    if (loading) {
      return <div>Loading...</div>
    }
    
    if (!user) {
      // Redirect to login
      return <Navigate to="/login" />
    }
    
    // User is authenticated, render component
    return <Component {...props} user={user} />
  }
}

// Usage:
function Dashboard({ user }) {
  return <h1>Welcome, {user.name}!</h1>
}

const ProtectedDashboard = withAuth(Dashboard)

// Dashboard only renders if user is logged in

2. withLoading - Loading State

pattern-loading.jsx
function withLoading(Component) {
  return function WithLoading({ isLoading, ...props }) {
    if (isLoading) {
      return (
        <div className="loading">
          <Spinner />
          <p>Loading...</p>
        </div>
      )
    }
    
    return <Component {...props} />
  }
}

// Usage:
function UserList({ users }) {
  return (
    <ul>
      {users.map(user => <li key={user.id}>{user.name}</li>)}
    </ul>
  )
}

const UserListWithLoading = withLoading(UserList)

function App() {
  const [users, setUsers] = React.useState([])
  const [loading, setLoading] = React.useState(true)
  
  // ... fetch users
  
  return <UserListWithLoading users={users} isLoading={loading} />
}

3. withLogger - Console Logging

pattern-logger.jsx
function withLogger(Component) {
  return function WithLogger(props) {
    React.useEffect(() => {
      console.log(`${Component.name} mounted with props:`, props)
      
      return () => {
        console.log(`${Component.name} unmounted`)
      }
    }, [props])
    
    React.useEffect(() => {
      console.log(`${Component.name} updated with props:`, props)
    })
    
    return <Component {...props} />
  }
}

// Usage:
const UserProfileWithLogger = withLogger(UserProfile)

// Logs all renders and props changes

4. withTheme - Theme Injection

pattern-theme.jsx
// Theme context
const ThemeContext = React.createContext()

function withTheme(Component) {
  return function WithTheme(props) {
    const theme = React.useContext(ThemeContext)
    
    return <Component {...props} theme={theme} />
  }
}

// Usage:
function Button({ theme, children }) {
  return (
    <button style={{ 
      backgroundColor: theme.primary,
      color: theme.text 
    }}>
      {children}
    </button>
  )
}

const ThemedButton = withTheme(Button)

// Button automatically gets theme from context

5. withData - Data Fetching

pattern-data.jsx
function withData(Component, fetchData) {
  return function WithData(props) {
    const [data, setData] = React.useState(null)
    const [loading, setLoading] = React.useState(true)
    const [error, setError] = React.useState(null)
    
    React.useEffect(() => {
      fetchData(props)
        .then(result => {
          setData(result)
          setLoading(false)
        })
        .catch(err => {
          setError(err.message)
          setLoading(false)
        })
    }, [props])
    
    if (loading) return <div>Loading...</div>
    if (error) return <div>Error: {error}</div>
    
    return <Component {...props} data={data} />
  }
}

// Usage:
function UserProfile({ data }) {
  return <div>{data.name}</div>
}

const UserProfileWithData = withData(
  UserProfile,
  (props) => fetch(`/api/users/${props.userId}`).then(r => r.json())
)

Interactive Demo

ReactPlayground.jsx
Code Editor
Preview

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

Composing Multiple HOCs

You can apply multiple HOCs to the same component:

compose-hocs.jsx
// Multiple HOCs
function UserProfile({ user, theme }) {
  return (
    <div style={{ color: theme.text }}>
      <h1>{user.name}</h1>
    </div>
  )
}

// āŒ Nested - Hard to read
const EnhancedUserProfile = withAuth(
  withTheme(
    withLogger(UserProfile)
  )
)

// āœ… Better - Use compose utility
function compose(...fns) {
  return (x) => fns.reduceRight((acc, fn) => fn(acc), x)
}

const enhance = compose(
  withAuth,
  withTheme,
  withLogger
)

const EnhancedUserProfile = enhance(UserProfile)

// āœ… Or apply one by one (clearest)
let EnhancedUserProfile = UserProfile
EnhancedUserProfile = withLogger(EnhancedUserProfile)
EnhancedUserProfile = withTheme(EnhancedUserProfile)
EnhancedUserProfile = withAuth(EnhancedUserProfile)

// All three approaches work!

HOCs vs Custom Hooks

When to Use HOCs:

  • Legacy codebases - Already using HOCs
  • Library APIs - Redux connect, React Router withRouter
  • Component wrapping - Need to wrap JSX
  • Class components - Can't use hooks

When to Use Custom Hooks:

  • New code - Hooks are the modern approach
  • Logic reuse - Easier to understand
  • Multiple values - Return many values easily
  • Function components - Natural fit

Same Logic, Different Approaches:

hoc-vs-hook.jsx
// HOC Approach
function withWindowWidth(Component) {
  return function WithWindowWidth(props) {
    const [width, setWidth] = React.useState(window.innerWidth)
    
    React.useEffect(() => {
      const handleResize = () => setWidth(window.innerWidth)
      window.addEventListener('resize', handleResize)
      return () => window.removeEventListener('resize', handleResize)
    }, [])
    
    return <Component {...props} windowWidth={width} />
  }
}

const MyComponentWithWidth = withWindowWidth(MyComponent)

// Custom Hook Approach (Better!)
function useWindowWidth() {
  const [width, setWidth] = React.useState(window.innerWidth)
  
  React.useEffect(() => {
    const handleResize = () => setWidth(window.innerWidth)
    window.addEventListener('resize', handleResize)
    return () => window.removeEventListener('resize', handleResize)
  }, [])
  
  return width
}

function MyComponent() {
  const width = useWindowWidth()
  return <div>Width: {width}</div>
}

// Hook version is simpler and more flexible!

Modern React Recommendation:

For new code, prefer custom hooks over HOCs.

Hooks are:
• Easier to understand
• More flexible
• Less nesting/wrapping
• Better TypeScript support
• Composable without wrapper hell

But still learn HOCs for:
• Reading existing code
• Using libraries that provide HOCs
• Maintaining legacy projects

Common HOC Mistakes

1. Using HOCs Inside Render

mistake-render.jsx
// āŒ Wrong - Creates new component every render!
function MyComponent() {
  const EnhancedComponent = withAuth(SomeComponent)
  return <EnhancedComponent />
}

// Problem: EnhancedComponent is recreated every render
// React sees it as a different component and remounts it

// āœ… Correct - Create outside component
const EnhancedComponent = withAuth(SomeComponent)

function MyComponent() {
  return <EnhancedComponent />
}

2. Not Copying Static Methods

mistake-static.jsx
// Original component with static method
function MyComponent() {
  return <div>Component</div>
}
MyComponent.staticMethod = function() {
  console.log('Static method')
}

// āŒ Wrong - Static method lost!
function withEnhancement(Component) {
  return function Enhanced(props) {
    return <Component {...props} />
  }
}

const Enhanced = withEnhancement(MyComponent)
Enhanced.staticMethod()  // Error! Undefined

// āœ… Correct - Copy static methods
function withEnhancement(Component) {
  function Enhanced(props) {
    return <Component {...props} />
  }
  
  // Copy static methods
  Enhanced.staticMethod = Component.staticMethod
  
  return Enhanced
}

// āœ… Or use library like hoist-non-react-statics
import hoistNonReactStatics from 'hoist-non-react-statics'

function withEnhancement(Component) {
  function Enhanced(props) {
    return <Component {...props} />
  }
  
  return hoistNonReactStatics(Enhanced, Component)
}

3. Prop Name Collisions

mistake-collisions.jsx
// āŒ Wrong - HOC adds prop that component already uses
function withData(Component) {
  return function WithData(props) {
    const data = fetchData()  // HOC's data
    return <Component {...props} data={data} />
  }
}

function MyComponent({ data }) {
  // Which data? HOC's or parent's?
  return <div>{data}</div>
}

// āœ… Better - Use unique prop names
function withData(Component) {
  return function WithData(props) {
    const fetchedData = fetchData()
    return <Component {...props} fetchedData={fetchedData} />
  }
}

function MyComponent({ data, fetchedData }) {
  // Clear which is which
  return <div>{data} {fetchedData}</div>
}

Key Takeaways

  • HOCs are functions that take a component and return an enhanced component
  • Pattern: HOC(Component) → EnhancedComponent
  • Name HOCs with "with" prefix (withAuth, withTheme)
  • Always pass through original props with spread operator
  • Don't mutate the original component
  • Set displayName for better debugging
  • Common patterns: auth, loading, logging, theme, data fetching
  • Can compose multiple HOCs together
  • Don't create HOCs inside render method
  • Copy static methods to enhanced component
  • Avoid prop name collisions
  • Modern React prefers custom hooks over HOCs
  • Still useful for legacy code and certain libraries

What's Next?

You now understand Higher-Order Components and when to use them! You know how to enhance components with reusable logic using the HOC pattern.

In the next lesson, you'll learn about Render Props Pattern:

  • What are render props
  • Using render props for code sharing
  • Render props vs HOCs vs hooks
  • Common render props patterns
  • Best practices and pitfalls

Get ready to learn the final classic React pattern! šŸŽÆ

Learning how to enhance components with Higher-Order Components in React!

Previous
Custom Hooks
Next
Render Props Pattern

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