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)}
The Response.ok property API states that:
Response.okRead onlyA 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
Responsestream and reads it to completion. It returns a promise that resolves with the result of parsing the body text asJSON.
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>
)
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With