Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using React forwardRef with Typescript generic JSX arguments

Tags:

Given the following typed React component using generic type arguments, how would I go about wrapping it in React's new forwardRef API?

type Props<T> = {
  forwardedRef?: Ref<HTMLInputElement>
  ...
}

class GenericComponent<T> extends Component<Props<T>> {
  ...
}

const ComponentWithRef = forwardRef<HTMLInputElement, Props<T>>((props, ref) => (
  <StringInput<T> {...props} forwardedRef={ref} />
))

The above approach has no way to define the T generic.

like image 729
James Conkling Avatar asked Aug 16 '18 20:08

James Conkling


People also ask

How do you use React forwardRef in functional components?

React forwardRef is a method that allows parent components pass down (i.e., “forward”) refs to their children. Using forwardRef in React gives the child component a reference to a DOM element created by its parent component. This then allows the child to read and modify that element anywhere it is being used.

How do I import forwardRef into React?

On this case, we need to change the Input Component to use the forwardRef. import React, { forwardRef } from "react"; const Input = (props, ref) => <input ref={ref} type="text" style={style} />; export default forwardRef(Input);

Is React forwardRef a hoc?

forwardRef() which means we have to apply the HOC before React.


1 Answers

So, to broaden the question some, this is really a question about preserving generic types in higher order functions. The following usage of forwardRef will properly typecheck (in 3.0.1)

const SelectWithRef = forwardRef(<Option extends string>(props: Props<Option>, ref?: Ref<HTMLSelectElement>) =>
  <Select<Option> {...props} forwardedRef={ref} />);

But, the Option generic is immediately resolved to string, rather than remaining as a generic. As such, the following does not typecheck

const onChange: (value: 'one' | 'two') => void = (value) => console.log(value);

<SelectWithRef<'one' | 'two'>
              ^^^^^^^^^^^^^^^ [ts] Expected 0 type arguments, but got 1
  value="a"
  options={['one', 'two']}
  onChange={onChange}
           ^^^^^^^^^^ [ts] Type 'string' is not assignable to type '"one" | "two"'
/>

The relevant issue is tracked in this Typescript issue ticket.

like image 78
James Conkling Avatar answered Sep 28 '22 05:09

James Conkling