You might be thinking:
"Max - it's 2020! Why are you still using HoCs instead of hooks?!"
to which I'd reply
"Saying 'It's $CURRENT_YEAR...' is incredibly cliché at this point, and contributes to the JavaScript community's churn. Write boring code."
So anyway, I came across a situation 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;
}
However for TypeScript, typing a HoC can become a little bit confusing, especially if you read some of the blog posts out there. If you tried to run TypeScript against the code above, you'd need to fix a few things:
- Both
WrappedComponent
andprops
have an implicitany
type - 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.
If this post helped you, I'd really appreciate a share on Twitter.
Top comments (1)
Thank you so much, Max. It was so helpful for me :)
Just I think the useState const should be inside of ComponentWithExtraInfo