⚛️ React: Hooks vs. Render Props vs. Higher-Order Components 👨‍🔬
Tags: react, javascript, webdev
Published: 2020-03-05 00:04:36

**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](https://codebetter.com.au), and as part of what I do, I post videos on our YouTube channel at [https://www.youtube.com/c/BetterCodingAcademy](https://www.youtube.com/c/BetterCodingAcademy).

_(Subscribe for awesome web development content!)_

The following content was sourced from the [Better Coding Academy style guide](https://github.com/better-coding-academy/style-guide).

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

```jsx
// #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);
```

**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:

    ```jsx
    <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:

    ```jsx
    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:

```jsx
<Mouse>
  {({ x, y }) => (
    <Page>
      {({ x: pageX, y: pageY }) => {
        // ^ big brain
      }}
    </Page>
  )}
</Mouse>
```

## 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:

    ```jsx
    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:

    ```jsx
    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:

    ```jsx
    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:

    ```jsx
    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](https://www.youtube.com/c/BetterCodingAcademy) for tons of free, full-length React, JavaScript, Node.js and general web development tutorials.

Happy coding!