Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I catch and parse errors properly in my React form submission handler?

I'm using React 16.13.0. I have the following function to deal with submit events:

handleFormSubmit(e) {
  e.preventDefault();
  const NC = this.state.newCoop;
  delete NC.address.country;

  fetch('/coops/',{
      method: "POST",
      body: JSON.stringify(this.state.newCoop),
      headers: {
        'Accept': 'application/json',
        'Content-Type': 'application/json'
      },
  }).then(response => {
      if (response.ok) {
          return response.json();
      }
      console.log(response.json());
      console.log(response.body);
      throw new Error(response.statusText);
  }).catch(errors => {
      console.log(errors);
      this.setState({ errors });
  });
}

However, I'm having an issue getting the errors properly from the response. When an error occurs, my endpoint returns a 400 request with the error text. This is what happens in curl:

curl --header "Content-type: application/json" --data "$req" --request POST "http://localhost:9090/coops/"
{"phone":["The phone number entered is not valid."]}

But the response.statusText contains "400 (Bad Request)". What's the right way to catch the error text and preserve it for future parsing? If my endpoint needs to format the data differently, what should it do (using Django/Python 3.7)?

Edit:

This is the Input component in which I'm trying to display errors:

<Input inputType={'text'}
    title = {'Phone'}
    name = {'phone'}
    value = {this.state.newCoop.phone}
    placeholder = {'Enter phone number'}
    handleChange = {this.handleInput}
    errors = {this.state.errors}
/> 

And the code of the input component, src/Input.jsx

import React from 'react';
import {FormControl, FormLabel} from 'react-bootstrap';

const Input = (props) => {
  return (
    <div className="form-group">
      <FormLabel>{props.title}</FormLabel>
      <FormControl
          type={props.type}
          id={props.name}
          name={props.name}
          value={props.value}
          placeholder={props.placeholder}
          onChange={props.handleChange}
      />

      {props.errors && props.errors[props.name] && (
          <FormControl.Feedback>
              <div className="fieldError">
                  {props.errors[props.name]}
              </div>
          </FormControl.Feedback>
      )}
    </div>
  )
}

export default Input;

When I run console.log(errors) they appear as such:

{phone: Array(1), web_site: Array(1)}
like image 642
Dave Avatar asked Jan 23 '26 15:01

Dave


1 Answers

The Response.ok property API states that:

Response.ok Read only

A boolean indicating whether the response was successful (status in the range 200–299) or not.

That means that even response.ok is false, the response.json() will return the data.

Body.json()

Takes a Response stream and reads it to completion. It returns a promise that resolves with the result of parsing the body text as JSON.

So, in your code, you should define your first fetch resolve to asynchronous and if the response it's not ok, then throw with the resolved response.json() using await:

handleFormSubmit(e) {
  e.preventDefault();
  const NC = this.state.newCoop;
  delete NC.address.country;

  fetch('/coops/',{
      method: "POST",
      body: JSON.stringify(this.state.newCoop),
      headers: {
        'Accept': 'application/json',
        'Content-Type': 'application/json'
      },
  }).then(async response => { // Define the first resolve to an asynchronous function
      if (response.ok) {
          // If it's OK, resolve JSON and return the actual data
          return await response.json();
          // or better set react state
          // const data = await response.json();
          // this.setState({ data });
      } else {
          // It's not OK, throw an error with the JSON data
          // so you'll be able to catch
          throw await response.json();
      }
  }).catch(errors => {
      // Here you should get the actual errors JSON response
      console.log(errors);
      this.setState({ errors });
  });
}

You can check the test example working using fetch-mock in this Stackblitz workspace.

If my endpoint needs to format the data differently, what should it do (using Django/Python 3.7)?

You'll have to let us know more about how your endpoint handles the requests by providing some code and explanation.

UPDATE

Regarding your component and displaying the errors, the result JSON returns an array of errors for each field. If you'll only one error, then change the endpoint to return a string instead of an array or display just the first error. If you'll have multiple errors, then you can map and render through all errors array for each field:

const Input = (props) => {
  return (
    <div className="form-group">
      <FormLabel>{props.title}</FormLabel>
      <FormControl
          type={props.type}
          id={props.name}
          name={props.name}
          value={props.value}
          placeholder={props.placeholder}
          onChange={props.handleChange}
      />

      // If you just want to display the first error
      // then render the first element of the errors array
      {props.errors && props.errors[props.name] && (
        <FormControl.Feedback>
          <div className="fieldError">
            {props.errors[props.name][0]}
          </div>
        </FormControl.Feedback>
      )}

      // Or if you may have multiple errors regarding each field
      // then map and render through all errors
      {/*
      {props.errors && props.errors[props.name] && (
        <FormControl.Feedback>
          {props.errors[props.name].map((error, index) => (
            <div key={`field-error-${props.name}-${index}`} className="fieldError">
              {error}
            </div>
          ))}
        </FormControl.Feedback>
      )}
      */}
    </div>
  )
}
like image 147
Christos Lytras Avatar answered Jan 26 '26 06:01

Christos Lytras



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!