Search code examples
javascriptreactjstypescriptreact-bootstrap

TypeScript Reusable Form with React Bootstrap


I'm following this post to learn react and typescript forms. There is a reusable form with React Bootstrap explained which I like to implement. I have tried to do so by writing the user form like below. But I' am getting

TS7031: Binding element 'cancel' implicitly has an 'any' type

and I don't know how to use it once i have solved this error. If I want for example to implement a sign-in form and a log-in form how do i refer to the user form below

import React from 'react';
import Form from 'react-bootstrap/Form';
import Button from 'react-bootstrap/Button';
import styled from 'styled-components';

const UserForm =
    ({
        cancel,
        errors,
        submit,
        submitButtonText,
        elements,
        passwordErrors
    }) => {

    function handleSubmit(event) {
        event.preventDefault();
        submit();
    }

    function handleCancel(event) {
        event.preventDefault();
        cancel();
    }

    return (
        <React.Fragment>
            <ErrorsDisplay errors={errors} passwordErrors={passwordErrors} />
            <Form onSubmit={handleSubmit}>
                {elements()}
                <Button className="mr-1" variant="primary" type="submit">{submitButtonText}</Button>
                <Button className="mr-1" variant="secondary" onClick={handleCancel}>Cancel</Button>
            </Form>
        </React.Fragment>
    );
 };

function ErrorsDisplay({ errors, passwordErrors}) {
    let errorDisplay = null;
    if (errors.length) {
        errorDisplay = (
            <React.Fragment>
                <ValidiationLabel>Errors:</ValidiationLabel>
                <ValidiationUl>
                    {errors.map((error, i) => (
                        <li key={i}>{error}</li>
                    ))}
                </ValidiationUl>
            </React.Fragment>
        );
    } else if (!passwordErrors) {
        errorDisplay = (
            <React.Fragment>
                <ValidiationLabel>Errors:</ValidiationLabel>
                <ValidiationUl>{<li>Passwords must match</li>}</ValidiationUl>
            </React.Fragment>
        );
    }
    return errorDisplay;
}

const ValidiationUl = styled.div`
    color: red;
    padding: 15px 0 40px 10px;
  `;
const ValidiationLabel = styled.h2`
    color: #0069c0;
    font-size: 28px;
  `;
export default UserForm;

Solution

  • First, assuming that you are using typescript project as you said (.tsx file extension), you need to type the parameters for UserForm:

    // This is just an example, not necessary needs to be this exactly types for the parameters
    interface UserFormProps {
      cancel(): void; // cancel is a function that returns nothing
      submit(): void;
      errors: string[]; // errors its an array of strings
      passwordErrors: boolean;
      submitButtonText: string;
      elements(): ReactNode; // elements its a funtion that returns react nodes
    
    }
    
    // Here you are destructuring the object parameter of UserForm function.
    // You telling it that its an UserFormProps Type Object on ": UserFormProps"
    const UserForm = ({
      cancel,
      submit,
      elements,
      errors,
      submitButtonText,
      passwordErrors,
    }: UserFormProps) => { .....
    

    and for ErrorsDisplay function:

    interface ErrorsProps {
      errors: string[];
      passwordErrors: boolean;
    }
    
    function ErrorsDisplay({ errors, passwordErrors }: ErrorsProps) {...
    

    For your handle functions, you need to specify the type of event:

    // You are saying the handleSubmit function receives an FormEvent from a HTML Form Element
    function handleSubmit(event: React.FormEvent<HTMLFormElement>) { ....
    
    // You are saying the handleCancel function receives an MouseEvent from a HTML Button Element
    function handleCancel(event: React.MouseEvent<HTMLButtonElement>) { ....
    

    After doing this, you can use your UserForm anywhere, for example your sign-in page / log-in page.

    You just need to import it:

    import React from "react";
    import Form from "react-bootstrap/Form";
    // here you need to inform the path according to your project
    import UserForm from "./UserForms";
    
    const SignIn = () => {
      return (
        <UserForm
          // I'm setting the values ​​hardcoded just as example
          cancel={() => {console.log('cancel')}}
          submit={() => {console.log('submit')}}
          errors={[]}
          passwordErrors={false}
          submitButtonText="test"
          elements={() => (
            <>
              <Form.Group controlId="ControlId">
                <Form.Control
                  type="email"
                  name="email"
                  value={"[email protected]"}
                  placeholder={"email"}
                ></Form.Control>
                <Form.Control
                  type="password"
                  name="password"
                  value={"password"}
                  placeholder={"password"}
                ></Form.Control>
              </Form.Group>
            </>
          )}
        ></UserForm>
      );
    };
    
    export default SignIn;