DEV Community

loading...
Cover image for ⚛️ React: Hooks vs. Render Props vs. Higher-Order Components 👨‍🔬

⚛️ React: Hooks vs. Render Props vs. Higher-Order Components 👨‍🔬

bettercodingacademy profile image Better Coding Academy Updated on ・3 min read

About Me: I've been a professional web developer for just over 10 years now. I'm currently the lead web development instructor at Better Coding Academy, and as part of what I do, I post videos on our YouTube channel at https://www.youtube.com/c/BetterCodingAcademy.

(Subscribe for awesome web development content!)

The following content was sourced from the Better Coding Academy style guide.

When deciding between hooks, render props and higher-order components, always go with hooks wherever possible.

// #1 - best
const MyComponent = () => {
  const mousePosition = useMouse();

  // mousePosition.x, mousePosition.y
}

// #2 - not as good
const MyComponent = () => {
  return (
    <Mouse>
      {({ x, y }) => {
        // ...
      }}
    </Mouse>
  )
}

// #3 - bad
const MyComponent = ({ x, y }) => {
  // ...
}

export default withMouse(MyComponent);
Enter fullscreen mode Exit fullscreen mode

Why? Well, let's start with higher-order components (HOCs).

Why are higher-order components bad?

Higher order components are bad for two main reasons:

  1. They take up a fixed prop name, possibly removing other props. For example, imagine for above example #3 we want to include an x and y prop on the component:

    <MyComponent x="some value" y="some other value" />
    

    Both of these values will be overwritten by the values coming from the higher order component. This issue can also arise when you wish to use multiple higher order components:

    export default withMouse(withPage(MyComponent)); // if withMouse and withPage set the same props, there will be clashing issues
    
  2. They do not clearly identify the source of your data. withMouse(MyComponent) does not tell you which props are being included onto the component (if any), hence increasing the amount of time spent debugging and fixing up the code.

Okay then, now let's look at render props. Because render props give you data within a function parameter, you can freely rename it however you like. For example:

<Mouse>
  {({ x, y }) => (
    <Page>
      {({ x: pageX, y: pageY }) => {
        // ^ big brain
      }}
    </Page>
  )}
</Mouse>
Enter fullscreen mode Exit fullscreen mode

Okay, well what about render props?

However, render props still have their own issues:

  1. They don't allow you to use their data outside of the return statement. With the example above, you can't use the x and y values in any state variables, useEffect hooks, or any other functions within your component, because it's only accessible within the return statement.
  2. They get nested... really quickly. Imagine we have three render prop components within a given component:

    const MyComponent = () => {
      return (
        <Mouse>
          {({ x, y }) => (
            <Page>
              {({ x: pageX, y: pageY }) => (
                <Connection>
                  {({ api }) => {
                    // yikes
                  }}
                </Connection>
              )}
            </Page>
          )}
        </Mouse>
      )
    };
    

So now, onto the final (and best) solution!

How hooks solve all of these issues!

Hooks address all of the above issues.

  1. Hooks don't have any fixed prop names - you can rename however you like:

    const { x, y } = useMouse();
    const { x: pageX, y: pageY } = usePage();
    
  2. Hooks clearly identify the source of the data - in the example above, it's clear that x and y come from useMouse, and pageX and pageY come from usePage.

  3. Hooks allow you to access data outside of the return statement. For example, you can do stuff like:

    const { x: pageX, y: pageY } = usePage();
    
    useEffect(() => {
      // this runs whenever pageX or pageY changes
    }, [pageX, pageY]);
    
  4. Hooks don't get nested at all. With the above render prop monstrosity rewritten using hooks, the code would look something like:

    const { x, y } = useMouse();
    const { x: pageX, y: pageY } = usePage();
    const { api } = useConnection();
    

    Three lines of beautiful code.


Hope you guys enjoyed this comparison between three architectural patterns within React! Be sure to follow me on YouTube for tons of free, full-length React, JavaScript, Node.js and general web development tutorials.

Happy coding!

Discussion (15)

pic
Editor guide
Collapse
shaijut profile image
Shaiju T

Hi, 😄 May I know which software you used to record the videos. is it freeware. And did you used normal mobile headphones as mic ?

Collapse
bettercodingacademy profile image
Better Coding Academy Author

I use OBS to record the videos with an AT2020 mic :)

Collapse
shaijut profile image
Shaiju T

Actually I thought you are using some paid ones like Camtasia, But Good to hear that by using OBS your videos are clear. Have you configured some special settings in OBS ?

Thread Thread
bettercodingacademy profile image
Better Coding Academy Author

Nope - OBS is really good. Since it's screen recording there really isn't too much variation in quality; it's more important to have a good microphone setup (something I definitely can improve upon 😁)

Collapse
dance2die profile image
Sung M. Kim

Other problems with HoC are,

  1. Hard to get it right (displayName, static method hosting etc)
  2. Major PIA to get types right in TypeScript.
Collapse
bettercodingacademy profile image
Better Coding Academy Author

It does not clearly identify the source of your data - you have no idea where x and y come from :)

Collapse
vuesomedev profile image
Gábor Soós

They don't need to know, just like with Dependency Injection in Angular.

Thread Thread
bettercodingacademy profile image
Better Coding Academy Author

Drawing a similarity between Angular and React here isn't particularly beneficial.

In React, tracking down where a particular prop comes from within a nest of multiple HOCs is a huge pain and a massive code smell that Hooks has since addressed beautifully.

Thread Thread
vuesomedev profile image
Gábor Soós

Aren't we talking about the dependencies of a component?

I don't know why it is necessary to know the source of a dependency inside a component.

Collapse
nicolasini profile image
Nico S___

this is very useful, thank you

Collapse
bettercodingacademy profile image
Better Coding Academy Author

You're welcome Nico!

Collapse
hieudevx profile image
Collapse
jrdleto1 profile image
jrdleto1

However I choose Hooks, if I'm able to, I don't think your arguments on higher-order components are strong, as they had to be, anyway thanks.

Collapse
rohanfaiyazkhan profile image
Rohan Faiyaz Khan

Even though the codebase I'm working on right now is mostly with hooks, there are a couple of HOCs and render props thrown in. I think each of them have their appropriate use.