TypeScript and React: Pass component in props and return it from a function

10,383

ReactNode is React element type, while component prop is expected to be React component.

It likely should be:

export interface Props {
  component: React.ComponentType;
  handleSubmit(): void;
}

renderForm has to return a ReactNode, so I can't change ReactNode to ComponentType (the latter would resolve the JSX error).

component prop and renderForm return type aren't connected. The former is a component and the latter is an element.

Share:
10,383
J. Hesters
Author by

J. Hesters

Ask better questions.

Updated on July 16, 2022

Comments

  • J. Hesters
    J. Hesters almost 2 years

    I'm writing a Form component with React and TypeScript. I use Formik for the form logic. I want to pass the <Form /> component a specific form as a prop and render it. Here is what I tried:

    import { Formik, FormikProps } from "formik";
    import React, { PureComponent, ReactNode } from "react";
    
    export interface Props {
      component: ReactNode; // TODO: Change to one of LoginForm or RegisterForm.
      handleSubmit(): void;
    }
    
    export class Form extends PureComponent<Props> {
      renderForm = (formikProps: FormikProps<any>): ReactNode => {
        const { component: FormComponent } = this.props;
        return <FormComponent {...formikProps} />;
      };
    
      render() {
        const { handleSubmit } = this.props;
        return <Formik render={this.renderForm} />;
      }
    }
    
    export default Form;
    

    The problem is that the line where I return the <FormComponent /> throws the error:

    [ts] JSX element type 'FormComponent' does not have any construct or call signatures.
    

    renderForm has to return a ReactNode, so I can't change ReactNode to ComponentType (the latter would resolve the JSX error).

    How would one do this in TypeScript?

    Edit So I got it working by doing this (thanks to estus):

    import { Formik, FormikProps } from "formik";
    import React, { PureComponent, ReactElement } from "react";
    
    export interface Props {
      component: React.ComponentType<any>; // TODO: Change to one of LoginForm or RegisterForm.
      handleSubmit(): void;
    }
    
    export class Form extends PureComponent<Props> {
      renderForm = (formikProps: FormikProps<any>): ReactElement<any> => {
        const { component: FormComponent } = this.props;
        return <FormComponent {...formikProps} />;
      };
    
      render() {
        const { handleSubmit } = this.props;
        return <Formik render={this.renderForm} />;
      }
    }
    
    export default Form;