DEV Community

Ayako yk
Ayako yk

Posted on

Understanding Higher-Order Components in React: Pros, Cons, and Modern Alternatives

I recently came across an interview question about Higher-Order Components (HOCs) and their role in enhancing a component's functionality. While HOCs are a powerful and advanced technique, it's worth noting that they're no longer as commonly used in modern React. In fact, the latest React documentation has phased out detailed explanations of HOCs.

In this blog post, I'll explore what HOCs are, their advantages, and why they are no longer the recommended approach in contemporary React development.

Higher-Order Component (HOC)

[A] higher-order component is a function that takes a component and returns a new component.

const EnhancedComponent = higherOrderComponent(WrappedComponent);
Enter fullscreen mode Exit fullscreen mode

Legacy React Documentation

This example comes from the older React documentation. I've updated the example to use a functional component and summarized the explanation.

A CommentList component subscribes to an external data source to render a list of comments:

import React, { useEffect, useState } from "react";

function CommentList() {
    // "DataSource" is some global data source
    const [comments, setComments] = useState(DataSource.getComments());

    useEffect(() => {
        function handleChange() {
            setComments(DataSource.getcomments());
        }

        DataSource.addChangeListener(handleChange);

        return () => {
            DataSource.removeChangeListener(handleChange);
        };
    }, []);

    return (
        <div>
            {comments.map((comment) => (
                <Comment comment={comment} key={comment.id} />
            ))}
        </div>
    );
}

export default CommentList;
Enter fullscreen mode Exit fullscreen mode

A BlogPost Component subscribes to a single blog post:

import React, { useEffect, useState } from "react";


function BlogPost(props) {
    const [blogPost, setBlogPost] = useState(DataSource.getBlogPost(props.id));

    useEffect(() => {
        function handleChange() {
            setBlogPost(DataSource.getBlogPost(props.id))
        }

        DataSource.addChangeListener(handleChange)

        return () => {
            DataSource.removeChangeListener(handleChange)
        }
    }, [props.id]);

    return <TextBlock text={blogPost} />

}

export default BlogPost;
Enter fullscreen mode Exit fullscreen mode

DataSource would look something like this:

const DataSource = {
    getComments: () => {
      return [...];
    },
    addChangeListener: (callback) => {},
    removeChangeListener: (callback) => {}
  };

  export default DataSource;
Enter fullscreen mode Exit fullscreen mode

CommentList and BlogPost are not identical, but much of their implementation is the same. To simplify this repetition, we can create a function that abstracts these shared patterns.

// Custom Hook
export function useSubscription(selectData, props) {
    const [data, setData] = useState(selectData(DataSource, props));

    useEffect(() => {
        function handleChange() {
            setData(selectData(DataSource, props))
        }

        DataSource.addChangeListener(handleChange)

        return () => {
            DataSource.removeChangeListener(handleChange)
        }
    }, [props])

    return data
}


function withSubscription(WrappedComponent, selectData) {
    return function(props) {
        const data = useSubsctiption(selectData, props)
        return <WrappedComponent data={data} {...props} />
    }
}
Enter fullscreen mode Exit fullscreen mode

When CommentListWithSubscription and BlogPostWithSubscription are rendered, they will pass a data prop containing the most current information from DataSource to their respective components.

const CommentListWithSubscription = withSubscription(
    CommentList,
    (DataSource) => DataSource.getComments()
)

const BlogPostWithSubscription = withSubscription(
    BlogPost,
    (DataSource, props) => DataSource.getBlogPost(props.id)
)
Enter fullscreen mode Exit fullscreen mode

A Higher-Order Component (HOC) is a pure function that enhances the original component by wrapping it in a container component, allowing us to share logic across multiple components without side effects.

"Higher-order components are not commonly used in modern React code," noted the legacy documentation.

After researching the reasons, developers have pointed out several disadvantages:

Complexity: HOCs can create deeply nested wrappers, making code harder to read and debug.

Prop Collisions: HOCs manipulate props, which can lead to unintended conflicts and issues.

Hooks as an Alternative
Custom hooks offer a more concise and straightforward way to handle the same logic, effectively replacing the need for HOCs.

Some developers still use HOCs for tasks like authentication or error handling. It's important to understand both the pros and cons and stay updated on the latest trends, allowing us to engage in informed discussions with our team members.

Top comments (0)