How to make a functional React component with generic type?

Viewed 7032

I'm trying to make a React component which takes in a generic type parameter which will be part of its prop type.

I want a solution that would look something like this:

interface TestProps<T> {
  value: T;
  onChange: (newValue: T) => void;
}

const Test: React.FC<TestProps<T>> = (props) => (
  <span>{props.value}</span>
);

I have seen that there is support for this in TypeScript 2.9 and I'm on 4.3.5.

Usage of this would look like this:

const Usage: React.FC = () => (
  <div>
    <Test<Obj>
      value={{ name: 'test' }}
      onChange={(newValue) => {
        console.log(newValue.name);
      }}
    />
  </div>
);

Code sandbox: https://codesandbox.io/s/react-typescript-playground-forked-8hu13?file=/src/index.tsx

3 Answers

The easiest way is to make the generic FC a regular function, not an arrow function. (React.PropsWithChildren<> emulates what React.FC does to your props type.)

function Test<T>(props: React.PropsWithChildren<TestProps<T>>) {
    return <span>Some component logic</span>;
}

You need to rewrite your Test component in this way

const Test= <T,>(props:TestProps<T>) => (
    <span>Some component logic</span>
);

Can you show the same with React.FC<TestProps>? It is impossible to do with FC.

This is FC implementation:

interface FunctionComponent<P = {}> {
  (props: PropsWithChildren<P>, context?: any): ReactElement<any, any> | null;
  // ... other static properties
}

As you might have noticed, FC is a function type, not type of props.

UPDATE

You can create higher order function, but I'm not sure if it worth it

const WithGeneric = <T,>(): React.FC<TestProps<T>> =>
  (props: TestProps<T>) => (
    <span>Some component logic</span>
  );
const Test = WithGeneric<Obj>()

In my case it was like the following codes:

export interface FormProps<T> {
  validator?: AnyObjectSchema;
  onSubmit?: (data: T) => void;
}

const Form = <T = any,>({
  children,
  validator,
}: PropsWithChildren<FormProps<T>>): JSX.Element => {
  ~~~

And in usage:

type MyType = ...

<Form<MyType> ...
Related