Use Hooks In Class Components Too
Tags: react, hooks
Published: 2019-10-12 20:26:22

With the official release of hooks, everybody seems to be writing function components exclusively, some even started refactoring all of their old class components. However class components are here to stay. We can't use hooks everywhere (yet), but there are some easy solutions.

# Higher Order Components

Higher Order Components (or HOCs) are functions that takes a `Component` in its arguments, and returns a `Component`. Before hooks, HOCs are often used to extract common logic from the app.

A simple HOC with a `useState` hook looks like this:

```js
const withFoo = (Component) => {
  function WithFoo(props) {
    const [foo, setFoo] = useState(null);

    return <Component foo={foo} setFoo={setFoo} {...props} />
  }

  WithFoo.displayName = `withFoo(${Component.displayName})`;

  return WithFoo;
};
```

Here, our `withFoo` function, can be called with a `Component`. Then, it returns a new `Component` that receives an additional prop `foo`. The `WithFoo` (note the capitalized `With`) is actually a function component - that's why we can use Hooks!

A few quick notes before we move on:

* Personally I usually name my HOCs `with*`, just like we always use the pattern `use*` for hooks.
* Setting a `displayName` on the HOC is not necessary, but it is very helpful for debugging your app in `react-devtools`
* Usually I spread the original `props` last - this avoids overwriting props provided by the users of the component, while allowing the users to override the new fields easily.

## Our Custom Hook

How do apply this to [our `useGet` hook](https://dev.to/pallymore/refactoring-an-old-react-app-creating-a-custom-hook-to-make-fetch-related-logic-reusable-2cd9)?

Let's replace `useState` from the example above to `useGet` ... but wait, `useGet` needs to be called with `{ url }` - where do we get that? 🤔

For now let's assume the `url` is provided to the component in its props:

```js
const withGetRequest = (Component) => {
  function WithGetRequest(props) {
    const state = useGet({ url: props.url });

    return <Component {...state} {...props} />
  }

  WithGetRequest.displayName = `withGetRequest(${Component.displayName})`;

  return WithGetRequest;
};
```

This works, but at the same time, this means whoever uses our wrapped component will have to provide a valid `url` in its props. This is probably not ideal because often we build `url`s dynamically either based on some `id`s or in some cases, user inputs (e.g. In a `Search` component, we are probably going to take some fields from the component's `state`.)

One of the limitations of HOCs is they are often "static": meaning we can't change its behavior easily at run-time. Sometimes we can mitigate that by building "Higher Higher Order Components" (not an official name) like the `connect` function provided by `react-redux`:

```js
// connect() returns a HOC
const withConnectedProps = connect(mapStateToProps, mapDispatchToProps);

// we use that HOC to wrap our component
const ConnectedFoo = withConnectedProps(Foo);
```

So, if our resource's url relies on some fields from the from the props maybe we can build something like this:

```js
// first we take a function that will be called to build a `url` from `props`
const makeWithGetRequest = (urlBuilder) => {
  return withGetRequest = (Component) => {
    return function WithGetRequest(props) {
      const url = urlBuilder(props);
      const state = useGet({ url });

      return <Component {...state} {...props} />;
    }
  };
};
```

It's safe to assume that different components will have different logic for building the URLs they need. For example, to wrap an `ArticlePage` component:

```js
// we know articleId and categoryId will be provided to the component
const buildArticleUrl = ({ articleId, categoryId }) => {
  return `/categories/${categoryId}/articles/${articleId}`;
};

// now our enhanced component is using the `useGet` hook!
export default makeWithGetRequest(buildArticleUrl)(ArticlePage);
```

This seems nice, but it doesn't solve the problem of building `url` with the component's `state`. I think we are too fixated on this HOC idea. And when we examine it closely we will discover another flaws with this approach - we are relying on props with fixed names being provided to the component, this could lead to a couple of problems:

- **Name Collision**: Users of the enhanced component will have to be extra careful to not accidentally override props provided by HOCs
- **Clarity**: Sometimes the prop names are not descriptive. In our `ArticlePage` example above, the component will receive `data` and `error` in its props and it could be confusing to future maintainers.
- **Maintainability**: When we compose multiple HOCs, it becomes harder and harder to tell which props must be provided by the user? which props are from HOCs? which HOC?

Let's try something else.

# Render Props / Function as Child

Render Props and Function as Child are both very common react patterns and they are very similar to each other.

Render Props is a pattern where a component takes a function in its props, and calls that function as the result of its `render` (or conditionally, in advanced use cases).

An example with hooks looks like this:

```js
const Foo = ({ renderFoo }) => {
  const [foo, setFoo] = useState(null);

  return renderFoo({ foo, setFoo });
};

// to use it:
class Bar extends Component {
  // ...

  render () {
    return (
      <Foo
        renderFoo={
          ({ foo, setFoo }) => {
            // we have access to the foo state here!
          };
        }
      />
    );
  };
};
```

When we decide that the user should always provide that render function as `children`, then we are using the "Function as Child" pattern. Replacing `renderFoo` with `children` in our example above will allow us to use it this way:

```js
<Foo>
  {
    ({ foo, setFoo }) => {
      // now we can use foo state here
    }
  }
</Foo>
```

The two patterns here are often interchangeable - many devs prefer one over the other, and you can even use them at the same time to provide max flexibility, but that'll be a topic for another time.

Let's try this pattern with our `useGet` hook.

```js
// it takes two props: url and children, both are required.
const GetURL = ({ url, children }) => {
  const state = useGet({ url });

  return children(state); // children must be a function.
};


// now we can use it like this!
class Search extends Component {
  // ...

  render() {
    const { keyword } = this.state;

    return (
      <GetURL url={buildSearchUrl({ keyword })}>
      {
        ({ isLoading, data, error }) => {

          // render the search UI and results here!
        }
      }
      </GetURL>
    );
  }
}
``` 

Easy, right?

Function as Child & Render Props are not without trade-offs. They are more flexible than HOCs but now our original component's JSX is now nested in an inline function - making it a bit tricky to test when using the `shallow` renderer from `enzyme`. And what happens if we want to compose multiple hooks in a component? I wouldn't nest another function child inside an existing one.

# Wrapping Up

Now we have two ways of making hooks (re-)usable everywhere! If a hook doesn't rely on any dynamic inputs, I would go with the HOC solution; If you want to be more flexible, providing a component with Render Props / Function as Child would be a much better choice.

Next let's talk about testing our hooks & components with `jest`, `sinon` and `@testing-library/react-hooks`. 🎉