Understanding React HOC

React HOC (Higher Order Component) is a function which accepts components and returns components. The HOC is wrapping the passed component parameter and usually provides some additional data, additional render UI or both.

Higher Order Components – HOC

Higher Order Components are not part of react or any other third-party library, they can be used due to the compositional nature of react. They are very useful when we have repeating logic or data passing in our components. They can provide a generic way to handle shared logic and rendering. 

For instance, let’s say we have employee service, which fetches employee information.

As a result from employee service we get employee objects with the following structure:

const employee = {
    fullName: 'Freddie Mercury',
    department: 'Singer',
    salary: 'N/A',
    experienceMonths: 40,
    birthDate: '05.09.1946',
    raiseSalary: function (byAmount) {
        this.salary += byAmount;
        return this.salary;
    },
};

We also have two components that accept employee objects in their props. For the sake of simplicity we will use very basic html with no styles.

The first component (PublicEmployeeInformation) displays some basic information about given employee:

const PublicEmployeeInformation = ({
    employee,
    employeeId,
    fetchEmployeeInfo,
}) => {
 
    useEffect(() => {
        fetchEmployeeInfo(employeeId);
    }, [employeeId]);
 
    return (
        <div>
            <h4>{employee.fullName}</h4>
            <h5>Department: {employee.department}</h5>
            <h5>Experience: {employee.experienceMonths}</h5>
            <h5>Birth Date: {employee.birthDate}</h5>
        </div>
    );
};
 
const mapStateToProps = state => ({
    employee: state.employees.current
});
 
const mapDispatchToProps = {
    fetchEmployeeInfo
};
 
export default connect(mapStateToProps, mapDispatchToProps)(PublicEmployeeInformation);

And the second one (EmployeePerformanceReview) displays button as well, which can be used to perform an action on given employee:

const EmployeePerformanceReview = ({
    employee,
    employeeId,
    fetchEmployeeInfo,
}) => {
 
    useEffect(() => {
        fetchEmployeeInfo(employeeId);
    }, [employeeId]);
 
    const handleRaiseSalary = () => {
        employee.raiseSalary(500);
    };
 
    return (
        <div>
            <h4>{employee.fullName}</h4>
            <h5>Department: {employee.department}</h5>
            <h6>Salary: {employee.salary} USD</h6>
            <h5>Experience: {employee.experienceMonths}</h5>
            <button onClick={handleRaiseSalary}>Raise Salary</button>
        </div>
    );
};
 
const mapStateToProps = state => ({
    employee: state.employees.current
});
 
const mapDispatchToProps = {
    fetchEmployeeInfo
};
 
export default connect(mapStateToProps, mapDispatchToProps)(EmployeePerformanceReview);

We can spot identical situations in these components:

  • both accepts employeeId in their props
  • both perform useEffect to fetch employee info with employeeId
  • both render similar fields from employee object (fullName, department, experienceMonths)
  • both are connected to redux state

So when we have so much similarities and code redundancy, naturally comes the question:

How can we make it more simple?

One of the answers is by using Higher Order Components (HOC).

As we already said – a HOC is a function, which accepts component and returns component. So let’s begin building a HOC that will solve our example issue.

const withEmployeeData = Component => {
    const WrapperComponent = props => {
        return <Component {...props} />;
    };
    return WrapperComponent;
};

Let’s break it down so it can become more understandable:

  • on line 1 we declare a function that accepts Component as parameter
  • on line 2 we create a new Component (wrapperComponent), which accepts its props
  • on line 3 we return the accepted Component parameter passing its props
  • on line 5 we return the brand new component – wrapperComponent;

IMPORTANT – never mutate the Component parameters in the HOC – new Components should be returned instead.

Ok, we already created our base HOC, so now let’s make it fit our needs.

const withEmployeeData = Component => {
    const WrapperComponent = ({
        employee,
        employeeId,
        fetchEmployeeInfo,
        ...props
    }) => {
        useEffect(() => {
            fetchEmployeeInfo(employeeId);
        }, [employeeId]);
        return <Component {...props} employee={employee} />;
    };
    const mapStateToProps = state => ({
        employee: state.employees.current
    });
    const mapDispatchToProps = {
        fetchEmployeeInfo
    };
    return connect(mapStateToProps, mapDispatchToProps)(WrapperComponent);
};

After we have declared our HOC abstraction of fetching employeeData, we can now use it to simplify PublicEmployeeInformation and EmployeePerformanceReview.

const PublicEmployeeInformation = ({
    employee,
}) => {
    return (
        <div>
            <h4>{employee.fullName}</h4>
            <h5>Department: {employee.department}</h5>
            <h5>Experience: {employee.experienceMonths}</h5>
            <h5>Birth Date: {employee.birthDate}</h5>
        </div>
    );
};
// here we wrap the component before export it so it will receive employeeData from the HOC
export default withEmployeeData(PublicEmployeeInformation);

 

const EmployeePerformanceReview = ({
    employee,
}) => {
    const handleRaiseSalary = () => {
        employee.raiseSalary(500);
    };
 
    return (
        <div>
            <h4>{employee.fullName}</h4>
            <h5>Department: {employee.department}</h5>
            <h6>Salary: {employee.salary} USD</h6>
            <h5>Experience: {employee.experienceMonths}</h5>
            <button onClick={handleRaiseSalary}>Raise Salary</button>
        </div>
    );
};
// here we wrap our component before export it so it will receive employeeData from the HOC
export default withEmployeeData(EmployeePerformanceReview);

At this point we can wrap any component we want with withEmployeeData HOC unless it receives employeeId from the props. It will receive the employee object from the HOC in its props and further it’s data can be used inside. Let’s see how we use PublicEmployeeInformation and EmployeePerformanceReview components after they are wrapped with the withEmployeeData HOC.

const App = () => {
    const employeeId = 123;
    return (
        <>
            <PublicEmploymentInformation employeeId={employeeId} />
            <EmployeePerformanceReview employeeId={employeeId} />
        </>
    )
}

Wait! Isn’t this the same way we would normally use the components with their props? YES! Just provide the props to the component and firstly the HOC will receive the provided props, it will perform some actions and then it will return a brand new enhanced component, out of the box!

Conclusion

  • HOCs are functions that accept a react component and return a brand new enhanced react component.
  • They provide a clean way of enhancing components functionality (may be logic, UI, or both).
  • HOCs work out of the box, which means you can enhance components to work with HOC without changing their props contract.

Notes

  • be aware to not mutate the HOC’s param component and to further return mutated component, because this can cause unwanted side effects.
  • always return new component.

Written by Martin Shahonov for Motion Software
Photo by:Artem Sapegin, Ferenc Almasi

higher order components

x

Motion Software starts operating under the name of its parent company Exadel, taking effect on July 15th.

More Info

Motion Software uses cookies to improve site functionality, provide you with a better browsing experience, and to enable our partners to advertise to you. Detailed information on the use of cookies on this Site, and how you can decline them, is provided in our Cookie Policy Learn more about cookies, Opens in new tab. By using this Site or clicking on OK, you consent to the use of cookies.

OK