Jeggy
Jeggy

Reputation: 1650

How to make a functional React component with generic type?

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

Upvotes: 40

Views: 46165

Answers (4)

Koen van der Marel
Koen van der Marel

Reputation: 208

This is how you create a generic component using an arrow function.

const StyledDropdown = <T,>(props: React.PropsWithChildren<Props<T>>) => {
    return (
        <></>
    );
};

export default StyledDropdown;

Upvotes: 0

AmerllicA
AmerllicA

Reputation: 32757

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>
  validation={something}
  onSubmit={handleSomething}
>
  <SomeCompo />
  <AnotherSomeCompo />
</Form>

Upvotes: 16

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>()

Upvotes: 35

AKX
AKX

Reputation: 169416

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>;
}

Upvotes: 34

Related Questions