I have a simple component that's wrapped with react-redux and I have a ref to that component with forwardRef: true
, like so:
// Button.tsx
class Button extends React.Component {
// ...
}
// ...
export default connect(mapStateToProps, null, null, { forwardRef: true })(Button);
Now I have a parent component which renders the Button component
// ButtonParent.tsx
import Button from './Button';
class ButtonParent extends React.Component {
buttonRef: React.RefObject<Button>;
public constructor(props) {
this.buttonRef = React.createRef()
}
render() {
return (
<>
<Button ref={this.buttonRef}/>
</>
);
}
}
Please note that this is a minimal simplified repro of the issue without unnecessary typings.
The issue is with the type of buttonRef
. It classifies Button
(which is the default export from Button.tsx
) as a value, which is correct. It also suggests me to use typeof
, which is incorrect.
The issue stems from the fact that Button
is the HOC from redux's connect, but the ref's type is the actual Button
class component.
It is possible to solve this by renaming and re-exporting the Button
component, this way:
// Button.tsx
class _Button extends React.Component {
// ...
}
// ...
export default connect(mapStateToProps, null, null, { forwardRef: true })(_Button);
and then using buttonRef: React.RefObject<_Button>
.
But I wonder if there's an easier / cleaner way to do it, maybe redux somehow exports the wrapped component's type and I just don't know how?
Edit: a simpler extracting type:
type WrappedComponentType<C extends { WrappedComponent: any }> = InstanceType<C['WrappedComponent']>;
I asked the question almost a year ago and today I encountered a similar problem and eventually found this thread again. In the last year I've got better at TypeScript so I've decide to re-tackle this and try to solve this, fortunately I found a solution!
You can infer the type by using redux's ConnectedComponent
type with this helper:
import React from "react";
import { ConnectedComponent } from "react-redux";
type WrappedComponentType<C> = C extends ConnectedComponent<any, infer T>
? T extends { ref?: React.LegacyRef<infer WrappedComponent> | undefined }
? WrappedComponent
: never
: never;
and then the type of buttonRef
:
this.buttonRef = React.createRef<WrappedComponentType<typeof Button>>();
If you're using functional components:
let buttonRef = useRef<WrappedComponentType<typeof Button>>(null);