Skip to main content
Course/Module 7/Topic 3 of 4Advanced

Higher-Order Components (HOCs)

Learn Higher-Order Components for cross-cutting concerns and component composition

80 minBy Priygop TeamLast updated: Feb 2026

What are HOCs?

Higher-Order Components (HOCs) are functions that take a component and return a new component with additional props or behavior. They are useful for sharing logic between components.

Basic HOC Pattern

Example
// Basic HOC example
import React from 'react';

// HOC for adding loading state
function withLoading(WrappedComponent) {
    return function WithLoadingComponent(props) {
        const [loading, setLoading] = React.useState(false);
        const [data, setData] = React.useState(null);

        const fetchData = async () => {
            setLoading(true);
            try {
                const response = await fetch(props.url);
                const result = await response.json();
                setData(result);
            } catch (error) {
                console.error('Error fetching data:', error);
            } finally {
                setLoading(false);
            }
        };

        React.useEffect(() => {
            fetchData();
        }, [props.url]);

        if (loading) {
            return <div>Loading...</div>;
        }

        return <WrappedComponent {...props} data={data} />;
    };
}

// Component to be enhanced
function UserList({ data }) {
    if (!data) return <div>No data available</div>;

    return (
        <ul>
            {data.map(user => (
                <li key={user.id}>{user.name}</li>
            ))}
        </ul>
    );
}

// Enhanced component
const UserListWithLoading = withLoading(UserList);

// Usage
function App() {
    return <UserListWithLoading url="/api/users" />;
}

// HOC for authentication
function withAuth(WrappedComponent) {
    return function WithAuthComponent(props) {
        const [user, setUser] = React.useState(null);
        const [loading, setLoading] = React.useState(true);

        React.useEffect(() => {
            // Check if user is authenticated
            const checkAuth = async () => {
                try {
                    const token = localStorage.getItem('token');
                    if (token) {
                        const response = await fetch('/api/me', {
                            headers: { Authorization: `Bearer ${token}` }
                        });
                        if (response.ok) {
                            const userData = await response.json();
                            setUser(userData);
                        }
                    }
                } catch (error) {
                    console.error('Auth check failed:', error);
                } finally {
                    setLoading(false);
                }
            };

            checkAuth();
        }, []);

        if (loading) {
            return <div>Checking authentication...</div>;
        }

        if (!user) {
            return <div>Please log in to access this page</div>;
        }

        return <WrappedComponent {...props} user={user} />;
    };
}

// Protected component
function Dashboard({ user }) {
    return (
        <div>
            <h1>Welcome, {user.name}!</h1>
            <p>This is your dashboard</p>
        </div>
    );
}

const ProtectedDashboard = withAuth(Dashboard);

Advanced HOC Patterns

Example
// Advanced HOC with composition and configuration
import React from 'react';

// HOC for error boundaries
function withErrorBoundary(WrappedComponent) {
    return class ErrorBoundary extends React.Component {
        constructor(props) {
            super(props);
            this.state = { hasError: false, error: null };
        }

        static getDerivedStateFromError(error) {
            return { hasError: true, error };
        }

        componentDidCatch(error, errorInfo) {
            console.error('Error caught by boundary:', error, errorInfo);
        }

        render() {
            if (this.state.hasError) {
                return (
                    <div className="error-boundary">
                        <h2>Something went wrong</h2>
                        <button onClick={() => this.setState({ hasError: false })}>
                            Try again
                        </button>
                    </div>
                );
            }

            return <WrappedComponent {...this.props} />;
        }
    };
}

// HOC for performance monitoring
function withPerformanceMonitoring(WrappedComponent, componentName) {
    return function PerformanceMonitoredComponent(props) {
        const startTime = React.useRef(Date.now());

        React.useEffect(() => {
            const endTime = Date.now();
            const renderTime = endTime - startTime.current;
            console.log(`${componentName} rendered in ${renderTime}ms`);
        });

        return <WrappedComponent {...props} />;
    };
}

// HOC for data caching
function withCache(WrappedComponent, cacheKey) {
    return function CachedComponent(props) {
        const [cachedData, setCachedData] = React.useState(null);

        React.useEffect(() => {
            const cached = localStorage.getItem(cacheKey);
            if (cached) {
                try {
                    setCachedData(JSON.parse(cached));
                } catch (error) {
                    console.error('Failed to parse cached data:', error);
                }
            }
        }, []);

        const updateCache = (data) => {
            localStorage.setItem(cacheKey, JSON.stringify(data));
            setCachedData(data);
        };

        return (
            <WrappedComponent
                {...props}
                cachedData={cachedData}
                updateCache={updateCache}
            />
        );
    };
}

// HOC composition
function compose(...hocs) {
    return (Component) => {
        return hocs.reduce((acc, hoc) => hoc(acc), Component);
    };
}

// Usage with multiple HOCs
const EnhancedComponent = compose(
    withErrorBoundary,
    (Component) => withPerformanceMonitoring(Component, 'UserList'),
    (Component) => withCache(Component, 'user-list-cache')
)(UserList);

// HOC with configuration
function withConfig(config) {
    return function(WrappedComponent) {
        return function ConfiguredComponent(props) {
            return (
                <WrappedComponent
                    {...props}
                    config={config}
                />
            );
        };
    };
}

// Usage
const UserListWithConfig = withConfig({
    pageSize: 10,
    sortBy: 'name',
    enableSearch: true
})(UserList);

Additional Resources

Recommended Reading

  • React Design Patterns
  • Advanced React Patterns
  • Custom Hooks Best Practices

Online Resources

  • Compound Components Tutorial
  • Render Props Guide
  • HOC Patterns Examples
Chat on WhatsApp
Priygop - Leading Professional Development Platform | Expert Courses & Interview Prep