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