Carl Rippon

Building SPAs

Carl Rippon
BlogBooks / CoursesAbout
This site uses cookies. Click here to find out more

Building a React Form Component with TypeScript: Submitting

June 26, 2018
reacttypescript

This is the last post in a series of blog posts where we are building our own super simple form component in React and TypeScript. In the last post we encapsulated validation so that the consumer of our components needs to do a minumim amount of work to include some basic form validation. In this post we’ll submit our form to our web api.

Submitting the form to our web API

Our form is coming along nicely with all that nice validation.

It’s time to submit the form to a real web api. All we need to do is implement Form.submitForm that we created in the very first post:

/**
 * Submits the form to the http api
 * @returns {boolean} - Whether the form submission was successful or not
 */
private async submitForm(): Promise<boolean> {
  try {
    const response = await fetch(this.props.action, {
      method: "post",
      headers: new Headers({
        "Content-Type": "application/json",
        Accept: "application/json"
      }),
      body: JSON.stringify(this.state.values)
    });
    return response.ok;
  } catch (ex) {
    return false;
  }
}

We simply call our api using the fetch function, passing the values from the form and returning whether it was successful or not.

So, if we fill in and submit a form, the web api should be called … and if the call is successful we should get confirmation at the bottom of the form:

Submitted Contact Us Form

But what if the api errors? Let’s force an error in the api:

(this is from an asp.net core backend)

[Route("api/contactus")]
public class ContactUsController : Controller
{
  [HttpPost]
  public async Task<IActionResult> Post([FromBody]ContactUsPost contactUsPost)
  {
      throw new Exception("an error");
      ...
  }
}

This is what we get:

Submitted Contact Us Form Error

That’s perfect. However what if a validation error occurs on the server? I know we should try to replicate all the validation on the client to get a great UX but that doesn’t always happen …

Let’s fake a validation error in our api:

(again this is asp.net core code)

[Route("api/contactus")]
public class ContactUsController : Controller
{
  [HttpPost]
  public async Task<IActionResult> Post([FromBody]ContactUsPost contactUsPost)
  {
    ModelState.AddModelError("Notes", "These notes aren't good enough!");
    return BadRequest(ModelState);
    ...
  }
}

This is what we get:

Submitted Contact Us Form Error

Well that’s not perfect! Ideally we would show the specific validation error that is returned in the response. So, let’s get to work and improve our Form component …

In submitForm() we branch off if we receive a 400. In the new branch, we inspect the response and map any errors to the IErrors format that Form expects. We then update the errors state.

private async submitForm(): Promise<boolean> {
  try {
    const response = await fetch(this.props.action, {
      method: "post",
      headers: new Headers({
        "Content-Type": "application/json",
        Accept: "application/json"
      }),
      body: JSON.stringify(this.state.values)
    });
    if (response.status === 400) {      /* Map the validation errors to IErrors */      let responseBody: any;      responseBody = await response.json();      const errors: IErrors = {};      Object.keys(responseBody).map((key: string) => {        // For ASP.NET core, the field names are in title case - so convert to camel case        const fieldName = key.charAt(0).toLowerCase() + key.substring(1);        errors[fieldName] = responseBody[key];      });      this.setState({ errors });    }    return response.ok;
  } catch (ex) {
    return false;
  }
}

So, if we submit the form again to our api with the fake a validation error, we get: Submitted Contact Us Form Error

Now this is perfect!

Wrapping up

Submitting the form to the web api is a straightforward task. It’s worth including a little extra code to deal with any validation errors that have come back from the server that escaped the frontend to give a good user experience.

So, we’ve reached the end of this series and built a nice component to reduce the amount of boilerplate in our forms. I hope you have found this interesting and useful!

If you to learn more about using TypeScript with React, you may find my course useful:

Using TypeScript with React

Using TypeScript with React
Find out more

Want more content like this?

Subscribe to receive notifications on new blog posts and courses

Required
© Carl Rippon
Privacy Policy