0

I'm working on this code to submit a form using React with Typescript and Material-UI:

export default function OpenTicket(props: any) {
    const classes = useStyles();

    const formHandler = (e: React.FormEvent<HTMLFormElement>) => {
        e.preventDefault();
        console.log(e.target);
    };

    return (

            <Container>
                <Box m={5}>
                    <div className={classes.root}>
                      <form>

                            <TextField
                              id="outlined-full-width"
                              label="Name"                                                    
                              />
                            <Button
                               variant="contained">
                               Submit
                            </Button> 
                        </form>
                    </div>
                </Box>
            </Container>
    );
}

Request:

export interface TicketDTO {
    title?: string;
}

export async function postTicket(): Promise<AxiosResponse<TicketDTO[]>> {
  return await axios.post<TicketDTO[]>(
    `${baseUrl}/support/tickets/create`
  );
}

What is the proper way to implement a field validation and make a call when Submit button is pressed?

2
  • I know this might not be what you're expecting but normally when I'm working with forms I like to use react-hook-form, it allows you to do input validation, with a lot of options, plus it is a light library that integrates very easily with your code and external libraries. Commented Sep 20, 2021 at 17:34
  • 1
    Can you show me code example please? Commented Sep 20, 2021 at 18:21

1 Answer 1

2

To answer your comment, this is how you usually would use react-hook-form:

import { useForm } from "react-hook-form";

export default function App() {
  const {
    register,
    handleSubmit,
    formState: { errors }
  } = useForm();

  // This won't be run unless all the input validations are met.
  const onSubmit = (data) => console.log(data);

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input {...register("firstName", { required: true, maxLength: 20 })} />
      {errors.firstName && <span>This field is required</span>}
      <input {...register("lastName", { pattern: /^[A-Za-z]+$/i })} />
      {errors.lastName && <span>This field can't be a number</span>}
      <input type="number" {...register("age", { min: 18, max: 99 })} />
      {errors.age && <span>This needs to be between 18 and 99 range</span>}
      <input type="submit" />
    </form>
  );
}

The register function is the one that handles the validation, you can read all the available validations here.

https://react-hook-form.com/api/useform/register

I think this eliminates a lot of code duplication to handle the inputs using react state, plus it also makes the validation and integration with external UI libraries very easy.

I have this sandbox in case you want to see the code in action.

Edit:

To perform a post request like the one in your question, and using the typing that you used, you can do something like this:

import axios, { AxiosResponse } from "axios";
import { useForm } from "react-hook-form";
import "./styles.css";

interface TicketDTO {
  title: string;
}

async function postTicket(
  data: TicketDTO
): Promise<AxiosResponse<TicketDTO[]>> {
  return await axios.post<TicketDTO[]>(`baseUrl/support/tickets/create`);
}

export default function App() {
  const {
    register,
    handleSubmit,
    formState: { errors }
    // This generic will type the data response correctly.
    // So the errors object and register function will infer the types of TicketDTO.
  } = useForm<TicketDTO>();

  // This won't be run unless all the input validations are met.
  const onSubmit = async (data: TicketDTO) => {
  // This console.log won't give errors
  // console.log(data.title);
  // This console.log will give typing errors
  // console.log(data.randomValue);
    try {
      await postTicket(data);
    } catch (err) {
      console.log(err);
    }
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input {...register("title", { required: true, maxLength: 20 })} />
      {errors.title && <span>This field is required</span>}
      <input type="submit" />
    </form>
  );
}

Here's the sandbox and the post is not working because there's no valid base url, but it should do what you want to.

Edit without react hook form.

This example uses react state and as I mention is more work to do since you will have to do the validations by yourself.

import axios, { AxiosResponse } from "axios";
import React from "react";

import "./styles.css";

interface TicketDTO {
  title: string;
}

async function postTicket(
  data: TicketDTO
): Promise<AxiosResponse<TicketDTO[]>> {
  return await axios.post<TicketDTO[]>(`baseUrl/support/tickets/create`);
}

export default function App() {
 // You will have to define your custom fields in separate states or one object state. this uses a separate state per input.
  const [title, setTitle] = React.useState("");
  // You will have to define the errors as well.
  const [errors, setErrors] = React.useState<TicketDTO>({
    title: ""
  });
  const onChange = (event: React.ChangeEvent<HTMLInputElement>) =>
    setTitle(event.target.value);

  const onSubmit = async (event: React.FormEvent<HTMLFormElement>) => {
    // Prevents browser to load the document
    event.preventDefault();
    // Here you will have to do logic to handle all your inputs.
    if (title) {
      try {
        await postTicket({ title });
      } catch (err) {
        console.error(err);
      }
      setErrors({ title: "" });
    } else {
      setErrors({ title: "Title is required" });
    }
  };
  return (
    <form onSubmit={onSubmit}>
      <input name="title" value={title} onChange={onChange} />
      {errors.title && <span>This field is required</span>}
      <input type="submit" />
    </form>
  );
}

Also you can take advantage of the HTML5 native validations if you don't want to do the validations in react. so it is just a matter of doing this:

import axios, { AxiosResponse } from "axios";
import React from "react";

import "./styles.css";

interface TicketDTO {
  title: string;
}

async function postTicket(
  data: TicketDTO
): Promise<AxiosResponse<TicketDTO[]>> {
  return await axios.post<TicketDTO[]>(`baseUrl/support/tickets/create`);
}

export default function App() {
  const [title, setTitle] = React.useState("");
  const onChange = (event: React.ChangeEvent<HTMLInputElement>) =>
    setTitle(event.target.value);

  const onSubmit = async (event: React.FormEvent<HTMLFormElement>) => {
    // Prevents browser to load the document
    event.preventDefault();
      try {
        await postTicket({ title });
      } catch (err) {
        console.error(err);
      }
  };
  return (
    <form onSubmit={onSubmit}>
      {/* required prop makes usage of native html5 validation */}
      <input name="title" value={title} required onChange={onChange} />
      <input type="submit" />
    </form>
  );
}
Sign up to request clarification or add additional context in comments.

5 Comments

Can you show me also how to make the API POST call?
@PeterPenzov just updated the answer.
Thank you. It's much more clear. Just in case can you show me code example without this react-hook-form library, please? I'm not sure I'm allowed to use it in my project.
I will update, but keep in mind this is the most basic example and validations are handled by you in the submit function
Hey @PeterPenzov updated the answer with native html5 validation and custom react validation.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.