Prevent a React component from re-rendering with a HOC

Thomas Rubattel
4 min readDec 6, 2020

--

A HOC does wrap a React component and decorate it — Credits Kari Shea unsplash.com

What is HOC ?

Higher-Order Component is a pattern in React invented by Sebastian Markbåge in early 2015.

Even though HOC was first created in the context of Class component, HOC can also be used with Functional components.

HOC started to gain traction after Andrew Clark — co-author of Redux — released the utility library recompose in 2015 for helping in using this pattern in a React project.

Why HOC ?

The idea of HOC is to reuse code. HOC is a special case of decorator.

Thus, HOC produces a new component by extending an existing component without modifying that latter.

A nice property that HOC’s have, is that they compose well with each other, in the same way you compose functions in mathematics — k(x) = f(g(x)). Composition is one of the most important concept used in functional programming and React seems to more and more go in this direction.

Thanks to this composition property, HOC ensures the single-responsibility principle, as middleware do.

What is HOC’s state of the art ?

React 16.8 released in 2019 introduced the React Hooks, designed by Sebastian Markbåge. Like HOC, React Hooks are designed to reuse code. In contrast to HOC, React Hooks only works within a functional component.

HOC’s are clearly losing popularity to React Hooks. For example, popular libraries, such as Redux, were using HOC but moved to React Hooks. Another popular React library called React Router was supporting HOC and moved toward another pattern called Render props and now favours React Hooks.

However in certain cases, like the one we’ll discuss right below, it still make sense to use HOC.

Use case

Now let’s dive into the problem we would like to solve. For some reasons we want some components to just render a single time, namely upon mounting only. We would like this logic to be reusable.

The withNoRerendering HOC embeds that logic making use of the React.memo() function which came along with React 16.6.

// withNoRerendering.tsx
import { memo, ComponentType } from "react";

const withNoRerendering = <P extends {}>
(WrappedComponent: ComponentType<P>): ComponentType<P> =>
memo(
(props) => <WrappedComponent {...(props as P)} />,
(prevProps, nextProps) => true
);

export default withNoRerendering;

Applying the withNoRerendering HOC to any React component produces a new component having the property of not getting re-rendered by any means.

// anyComponent.tsx
import React, { FunctionComponent } from "react";
import withNoRerendering from "./HOC/withNoRerendering";

const anyComponent: FunctionComponent<{}> = (props) => (
<div>
Here is any Component, either container or dumb
</div>
);

export default withNoRerendering(anyComponent);

The HOC does also work with class components.

// anyClassComponent.tsx
import React, { Component } from "react";
import withNoRerendering from "./HOC/withNoRerendering";

class anyClassComponent extends Compoment<{}> {
render(){
return (
<div>
Here is any Component, either container or dumb
</div>
);
}
}

export default withNoRerendering(anyClassComponent);

Other use case: Function as props

As a reminder, a React component re-render if one of the conditions below is met.

a. Component’s parent re-render.
b. Component’s own state gets updated.

To prevent a component to unnecessary re-render when a component receives a function as props, useCallback in conjunction with memo can be used. Indeed, memo does a shallow comparison whereas useCallback does memoize a function.

In the snippet right below, two buttons are displayed in the UI. Clicking on one button do not re-render the other button.

// Parent.tsx
import { useState, useCallback } from "react";
import Counter from "./Counter";

function Parent() {
const [counter1, setCounter1] = useState(0);
const [counter2, setCounter2] = useState(0);

const onClick1 = useCallback(() => setCounter1((prev) => prev + 1), []);
const onClick2 = useCallback(() => setCounter2((prev) => prev + 1), []);

return (
<>
<Counter onClick={onClick1} value={counter1} />
<Counter onClick={onClick2} value={counter2} />
</>
);
}

export default Parent;

///////////////////////////////////////////////////////////////////////////

// Counter.tsx
import { FC, memo } from "react";

type CounterProps = {
value: number | string;
onClick: () => void;
};

const Counter: FC<CounterProps> = ({ value, onClick }) =>
<button onClick={onClick}>{value}</button>;

export default memo(
Counter,
(prevProps, nextProps) => prevProps.value === nextProps.value
&& prevProps.onClick === nextProps.onClick
);

Wrap up

It’s a fact, React Hooks have overtaken HOC. However, HOC’s still have use cases as the one we discussed in the present article.

While React Hooks are good at managing a component own states and forcing a component to re-render upon a state’s change, HOC excels at injecting new props and at component composing. Thus, a combination of both, HOC and React Hooks, might be a better alternative than custom Hook (see an example here).

Render props is another pattern which is a special case of inversion of control and thereby gives the full control over a component’s render to the parent.

All three patterns, HOC, render props and React Hooks will still have use cases in the future. HOC is a mere function, render props is nothing but a function and React Hooks are regular functions.

Dear Reader, as a conclusion, let’s have a question to you. In the withNoRerendering HOC, you might have noticed that we made use of the React.memo() function. Which pattern do you think that that latter function implements ? Response here.

--

--