Search code examples
reactjstypescriptmaterial-uireact-final-form

Type '(props: Props) => Element' is not assignable to type 'FunctionComponent<FieldRenderProps<any, HTMLElement>>' with React-final-form


I'm beginner to typescript and I'm trying to create a signUp form using React-Final-Form and Typescript. So I create my form which is described by:

import React from "react";
import Button from "@material-ui/core/Button";
import { Form, Field } from "react-final-form";
import { makeStyles, Theme, createStyles } from "@material-ui/core/styles";
import Grid from "@material-ui/core/Grid";
import Paper from "@material-ui/core/Paper";
import Typography from "@material-ui/core/Typography";
import PasswordField from "../../components/Password";
import TextField from "../../components/TextField";
import { validate, submit } from "./validation";

const useStyles = makeStyles((theme: Theme) =>
  createStyles({
    container: {
      padding: 16,
      margin: "auto",
      maxWidth: 600
    },
    paper: {
      padding: 16
    },
    item: {
      marginTop: 16
    }
  })
);

const SignUp = () => {
  const classes = useStyles();

  return (
    <div className={classes.container}>
      <Typography>SignUp Form</Typography>
      <Form
        onSubmit={submit}
        validate={validate}
        render={({ handleSubmit, form, submitting, pristine }) => (
          <form onSubmit={handleSubmit}>
            <Paper className={classes.paper}>
              <Grid container alignItems='flex-start' spacing={2}>
                <Grid item xs={6}>
                  <Field
                    fullWidth
                    required
                    name='Username'
                    component={TextField}
                    type='text'
                    label='Username'
                  />
                </Grid>
                <Grid item xs={6}>
                  <Field
                    fullWidth
                    required
                    name='Email'
                    component={TextField}
                    type='text'
                    label='Email'
                  />
                </Grid>
                <Grid item xs={6}>
                  <Field
                    fullWidth
                    required
                    name='Password'
                    component={PasswordField}
                    type='password'
                    label='Password'
                  />
                </Grid>
                <Grid item className={classes.item}>
                  <Button
                    type='button'
                    variant='contained'
                    onClick={form.reset}
                    disabled={submitting || pristine}
                  >
                    Reset
                  </Button>
                </Grid>
                <Grid item className={classes.item}>
                  <Button
                    variant='contained'
                    color='primary'
                    type='submit'
                    disabled={submitting}
                  >
                    Submit
                  </Button>
                </Grid>
              </Grid>
            </Paper>
          </form>
        )}
      />
    </div>
  );
};

export default SignUp;

Which needs the componet TextField and Password:

TextField.tsx:

import React from "react";
import { makeStyles, Theme, createStyles } from "@material-ui/core/styles";
import TextField from "@material-ui/core/TextField";

const useStyles = makeStyles((theme: Theme) =>
  createStyles({
    textField: {
      marginLeft: theme.spacing(1),
      marginRight: theme.spacing(1)
    }
  })
);

interface Props {
  label: string;
  value: string;
  handleChange: () => {};
  type: string;
}

const SimpleTextField = (props: Props) => {
  const classes = useStyles();
  const { label, value, handleChange, type } = props;

  return (
    <TextField
      id='outlined-name'
      label={label}
      className={classes.textField}
      value={value}
      onChange={handleChange}
      type={type}
      margin='normal'
      variant='outlined'
    />
  );
};

export default SimpleTextField;

However tslint returns this error:

Type '(props: Props) => Element' is not assignable to type '"textarea" | "input" | "select" | ComponentClass<FieldRenderProps<any, HTMLElement>, any> | FunctionComponent<FieldRenderProps<any, HTMLElement>> | undefined'.
  Type '(props: Props) => Element' is not assignable to type 'FunctionComponent<FieldRenderProps<any, HTMLElement>>'.
    Types of parameters 'props' and 'props' are incompatible.
      Type 'FieldRenderProps<any, HTMLElement> & { children?: ReactNode; }' is missing the following properties from type 'Props': label, value, handleChange, type  TS2322

    44 |                     required
    45 |                     name='Username'
  > 46 |                     component={TextField}
       |                     ^
    47 |                     type='text'
    48 |                     label='Username'
    49 |                   />

I tried to change the return of TextField by modifing the code like that:

import React from "react";
import { makeStyles, Theme, createStyles } from "@material-ui/core/styles";
import TextField from "@material-ui/core/TextField";

const useStyles = makeStyles((theme: Theme) =>
  createStyles({
    textField: {
      marginLeft: theme.spacing(1),
      marginRight: theme.spacing(1)
    }
  })
);

interface Props {
  label: string;
  value: string;
  handleChange: () => {};
  type: string;
}

const SimpleTextField = (props: Props) => {
  const classes = useStyles();
  const { label, value, handleChange, type } = props;

  return (
    <TextField
      id='outlined-name'
      label={label}
      className={classes.textField}
      value={value}
      onChange={handleChange}
      type={type}
      margin='normal'
      variant='outlined'
    />
  );
};

const TextFieldWrapper = (props: Props) => {
   return(
     <>
      <SimpleTextField {...props}/>
     </>
   )
}

export default TextFieldWrapper;

But still get the same issue.


Solution

  • import React from "react";
    import Button from "@material-ui/core/Button";
    import { Form, Field } from "react-final-form";
    import { makeStyles, Theme, createStyles } from "@material-ui/core/styles";
    import Grid from "@material-ui/core/Grid";
    import Paper from "@material-ui/core/Paper";
    import Typography from "@material-ui/core/Typography";
    import PasswordField from "../../components/Password";
    import TextField from "../../components/TextField";
    import { validate, submit } from "./validation";
    
    const useStyles = makeStyles((theme: Theme) =>
      createStyles({
        container: {
          padding: 16,
          margin: "auto",
          maxWidth: 600
        },
        paper: {
          padding: 16
        },
        item: {
          marginTop: 16
        }
      })
    );
    
    const SignUp = () => {
      const classes = useStyles();
    
      return (
        <div className={classes.container}>
          <Typography>SignUp Form</Typography>
          <Form
            onSubmit={submit}
            validate={validate}
            render={({ handleSubmit, form, submitting, pristine }) => (
              <form onSubmit={handleSubmit}>
                <Paper className={classes.paper}>
                  <Grid container alignItems='flex-start' spacing={2}>
                    <Grid item xs={6}>
                      <Field fullWidth required name='Username'>
                        {props => (
                          <TextField
                            label='Username'
                            type='text'
                            value={props.input.value}
                            onChange={props.input.onChange}
                          />
                        )}
                      </Field>
                    </Grid>
                    <Grid item xs={6}>
                      <Field fullWidth required name='Email'>
                        {props => (
                          <TextField
                            label='Email'
                            type='email'
                            value={props.input.value}
                            onChange={props.input.onChange}
                          />
                        )}
                      </Field>
                    </Grid>
                    <Grid item xs={6}>
                      <Field fullWidth required name='Password'>
                        {props => (
                          <PasswordField
                            label='Password'
                            type='password'
                            value={props.input.value}
                            onChange={props.input.onChange}
                          />
                        )}
                      </Field>
                    </Grid>
                    <Grid item className={classes.item}>
                      <Button
                        type='button'
                        variant='contained'
                        onClick={form.reset}
                        disabled={submitting || pristine}
                      >
                        Reset
                      </Button>
                    </Grid>
                    <Grid item className={classes.item}>
                      <Button
                        variant='contained'
                        color='primary'
                        type='submit'
                        disabled={submitting}
                      >
                        Submit
                      </Button>
                    </Grid>
                  </Grid>
                </Paper>
              </form>
            )}
          />
        </div>
      );
    };
    
    export default SignUp;