Max RozenMax Rozen
TwitterArticlesNewsletter

How to implement a Higher-order component in React with TypeScript

In TypeScript, typing a HoC can be a little bit confusing, especially if you read some of the blog posts out there.

I came across a situation at work in which I needed to use a React Hook in a class-based component. My initial instinct was to rewrite from a class component to a function component, however upon further inspection I realised it had 1100 lines of code, with relatively complicated lifecycle methods.

I decided to wrap the component with a functional HoC that called the Hook, and passed the result down to the component as a prop.

In normal JavaScript, it isn’t too complicated, you’d do something like this:

import React, { useState } from 'react';

export function withExtraInfo(WrappedComponent) {
  const [extraInfo, setExtraInfo] = useState('');

  const ComponentWithExtraInfo = (props) => {
    return <WrappedComponent {...props} extraInfo={extraInfo} />;
  };

  return ComponentWithExtraInfo;
}

If you tried to run TypeScript against the code above, you’d need to fix a few things:

  1. Both WrappedComponent and props have an implicit any type
  2. Make the function generic

Here’s how we’d do that:

import React, { useState } from 'react';

// First we need to add a type to let us extend the incoming component.
type ExtraInfoType = {
  extraInfo: string;
};
// Mark the function as a generic using P (or whatever variable you want)
export function withExtraInfo<P>(
  // Then we need to type the incoming component.
  // This creates a union type of whatever the component
  // already accepts AND our extraInfo prop
  WrappedComponent: React.ComponentType<P & ExtraInfoType>
) {
  const [extraInfo, setExtraInfo] = useState('');
  setExtraInfo('important data.');

  const ComponentWithExtraInfo = (props: P) => {
    // At this point, the props being passed in are the original props the component expects.
    return <WrappedComponent {...props} extraInfo={extraInfo} />;
  };
  return ComponentWithExtraInfo;
}

You’ll probably notice we marked withExtraInfo as a generic using <P>. For more information, see the TypeScript Handbook.

To wrap things up, you’ll want to add a displayName to your HoC, which I’ve left as an exercise for the reader.

Do you struggle to keep up with best practices in React?

I send a single email weekly with an article like this one to help improve the quality of your React apps. Lots of developers like them, and I'd love to hear what you think as well. You can always unsubscribe.

    Join 43 React developers who signed up last week!

    rssTwitterGitHub

    © 2020 Max Rozen. All rights reserved.