在我的 React 应用程序中,我在哪里处理服务器返回的表单验证错误?
Where do I handle form validation errors returned by the server in my React app?
我正在使用 React 16.12.0。我试图弄清楚如何处理和显示从我的服务器返回的验证错误。我已经设置了这个表格...
import React, {Component} from 'react';
import {FormControl, FormGroup} from 'react-bootstrap';
/* Import Components */
import Input from '../components/Input';
import Country from '../components/Country';
import Province from '../components/Province';
import Button from '../components/Button'
class FormContainer extends Component {
statics: {
DEFAULT_COUNTRY: 484;
}
constructor(props) {
super(props);
this.state = {
countries: [],
provinces: [],
newCoop: {
name: '',
type: {
name: ''
},
address: {
formatted: '',
locality: {
name: '',
postal_code: '',
state: ''
},
country: 484, //FormContainer.DEFAULT_COUNTRY,
},
enabled: true,
email: '',
phone: '',
web_site: ''
},
}
this.handleFormSubmit = this.handleFormSubmit.bind(this);
this.handleClearForm = this.handleClearForm.bind(this);
this.handleInput = this.handleInput.bind(this);
}
/* This life cycle hook gets executed when the component mounts */
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 => {
response.json().then(data =>{
console.log("Successful" + data);
})
})
}
handleClearForm() {
// Logic for resetting the form
}
handleInput(e) {
let self=this
let value = e.target.value;
console.log("value:" + value);
let name = e.target.name;
//update State
this.setValue(self.state.newCoop,name,value)
}
setValue = (obj,is, value) => {
if (typeof is == 'string')
return this.setValue(obj,is.split('.'), value);
else if (is.length === 1 && value!==undefined)
return this.setState({obj: obj[is[0]] = value});
else if (is.length === 0)
return obj;
else
return this.setValue(obj[is[0]],is.slice(1), value);
}
render() {
return (
<form className="container-fluid" onSubmit={this.handleFormSubmit}>
<FormGroup
controlId="formBasicText">
<Input inputType={'text'}
title= {'Name'}
name= {'name'}
value={this.state.newCoop.name}
placeholder = {'Enter cooperative name'}
handleChange = {this.handleInput}
/> {/* Name of the cooperative */}
<Input inputType={'text'}
title= {'Type'}
name= {'type.name'}
value={this.state.newCoop.type.name}
placeholder = {'Enter cooperative type'}
handleChange = {this.handleInput}
/> {/* Type of the cooperative */}
<Input inputType={'text'}
title= {'Street'}
name= {'address.formatted'}
value={this.state.newCoop.address.formatted}
placeholder = {'Enter address street'}
handleChange = {this.handleInput}
/> {/* Address street of the cooperative */}
<Input inputType={'text'}
title= {'City'}
name= {'address.locality.name'}
value={this.state.newCoop.address.locality.name}
placeholder = {'Enter address city'}
handleChange = {this.handleInput}
/> {/* Address city of the cooperative */}
<Country title={'Country'}
name={'address.country'}
options = {this.state.countries}
value = {this.state.newCoop.address.country}
placeholder = {'Select Country'}
handleChange = {this.handleInput}
/> {/* Country Selection */}
<Province title={'State'}
name={'address.locality.state'}
options = {this.state.provinces}
value = {this.state.newCoop.address.locality.state}
placeholder = {'Select State'}
handleChange = {this.handleInput}
/> {/* State Selection */}
<Input inputType={'text'}
title= {'Postal Code'}
name= {'address.locality.postal_code'}
value={this.state.newCoop.address.locality.postal_code}
placeholder = {'Enter postal code'}
handleChange = {this.handleInput}
/> {/* Address postal code of the cooperative */}
<Input inputType={'text'}
title= {'Email'}
name= {'email'}
value={this.state.newCoop.email}
placeholder = {'Enter email'}
handleChange = {this.handleInput}
/> {/* Email of the cooperative */}
<Input inputType={'text'}
title= {'Phone'}
name= {'phone'}
value={this.state.newCoop.phone}
placeholder = {'Enter phone number'}
handleChange = {this.handleInput}
/> {/* Phone number of the cooperative */}
<Input inputType={'text'}
title= {'Web Site'}
name= {'web_site'}
value={this.state.newCoop.web_site}
placeholder = {'Enter web site'}
handleChange = {this.handleInput}
/> {/* Web site of the cooperative */}
<Button
action = {this.handleFormSubmit}
type = {'primary'}
title = {'Submit'}
style={buttonStyle}
/> { /*Submit */ }
<Button
action = {this.handleClearForm}
type = {'secondary'}
title = {'Clear'}
style={buttonStyle}
/> {/* Clear the form */}
</FormGroup>
</form>
);
}
componentDidMount() {
let initialCountries = [];
let initialProvinces = [];
// Get initial countries
fetch('/countries/')
.then(response => {
return response.json();
}).then(data => {
initialCountries = data.map((country) => {
return country
});
console.log("output ...");
console.log(initialCountries);
this.setState({
countries: initialCountries,
});
});
// Get initial provinces (states)
fetch('/states/484/')
.then(response => {
return response.json();
}).then(data => {
console.log(data);
initialProvinces = data.map((province) => {
return province
});
this.setState({
provinces: initialProvinces,
});
});
}
}
const buttonStyle = {
margin : '10px 10px 10px 10px'
}
export default FormContainer;
当服务器(Django Python 应用程序)无法处理表单时,它会 returns 一个 400 响应正文,错误链接到每个字段。例如,这样的响应主体看起来像
{"phone":["The phone number entered is not valid."]}
捕获此错误并将其显示在我的表单中的正确方法是什么?我见过的所有示例都在提交表单之前处理验证——例如在结束之前在 "handleFormSubmit" 处理程序中编写一个 "validateFields" 方法或类似的东西。
编辑: 添加了新的提取方法以响应评论。但是,当获取请求返回 400 时,添加 "catch" 块不会显示错误。
fetch('/coops/',{
method: "POST",
body: JSON.stringify(this.state.newCoop),
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
}).then(response => {
response.json().then(data =>{
console.log("Successful" + data);
})
}).catch(error => console.log(error))
CHrome 开发控制台报告请求 400 以及
FormContainer.jsx:54 Fetch failed loading: POST "http://localhost:3000/coops/".
此外,尽管有 400 和 Chrome 报告的内容,"Success" console.log 消息仍会打印出来。
正在处理表单提交错误响应。
fetch('/coops/',{
method: "POST",
body: JSON.stringify(this.state.newCoop),
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
}).then(response => {
// check if received an OK response, throw error if not
if (response.ok) {
return response.json();
}
throw new Error(response.statusText);
}).then(jsonData => {
console.log("Successful", jsonData);
}).catch(errors => {
// If error was thrown then unpack here and handle in component/app state
console.log('error', errors);
this.setState({ errors });
});
显示一个简单的错误列表(假设所有错误都是形状 {[string]: arrayOf(string)}
)
render() {
const errors = Object.values(this.state.errors);
return (
<form
className="container-fluid"
onSubmit={this.handleFormSubmit}
>
<FormGroup controlId="formBasicText">
{errors.length && (
<ul>
{errors.map(error => <li>{error.join(" ")}</li>}
</ul>
)}
<Input
inputType={'text'}
title= {'Name'}
name= {'name'}
value={this.state.newCoop.name}
placeholder = {'Enter cooperative name'}
handleChange = {this.handleInput}
/> {/* Name of the cooperative */}
...
显示字段级错误
render() {
const { errors } = this.state;
return (
<form
className="container-fluid"
onSubmit={this.handleFormSubmit}
>
<FormGroup controlId="formBasicText">
<Input
inputType={'text'}
title= {'Name'}
name= {'name'}
value={this.state.newCoop.name}
placeholder = {'Enter cooperative name'}
handleChange = {this.handleInput}
/> {/* Name of the cooperative */}
{errors.name && (
<div className="fieldError">
{error.name.join(" ")}
</div>
)}
<Input
inputType={'text'}
title= {'Type'}
name= {'type.name'}
value={this.state.newCoop.type.name}
placeholder = {'Enter cooperative type'}
handleChange = {this.handleInput}
/> {/* Type of the cooperative */}
{errors.name && (
<div className="fieldError">
{error['type.name"].join(" ")}
</div>
)}
...
后者是一种常见的模式,许多表单库直接将表单错误作为 prop 并自行处理显示错误帮助文本。许多处理两者的形式,使用保留的表单级错误键,即 _error
,然后是所有字段级错误,它们使用字段名作为它们的键。
我正在使用 React 16.12.0。我试图弄清楚如何处理和显示从我的服务器返回的验证错误。我已经设置了这个表格...
import React, {Component} from 'react';
import {FormControl, FormGroup} from 'react-bootstrap';
/* Import Components */
import Input from '../components/Input';
import Country from '../components/Country';
import Province from '../components/Province';
import Button from '../components/Button'
class FormContainer extends Component {
statics: {
DEFAULT_COUNTRY: 484;
}
constructor(props) {
super(props);
this.state = {
countries: [],
provinces: [],
newCoop: {
name: '',
type: {
name: ''
},
address: {
formatted: '',
locality: {
name: '',
postal_code: '',
state: ''
},
country: 484, //FormContainer.DEFAULT_COUNTRY,
},
enabled: true,
email: '',
phone: '',
web_site: ''
},
}
this.handleFormSubmit = this.handleFormSubmit.bind(this);
this.handleClearForm = this.handleClearForm.bind(this);
this.handleInput = this.handleInput.bind(this);
}
/* This life cycle hook gets executed when the component mounts */
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 => {
response.json().then(data =>{
console.log("Successful" + data);
})
})
}
handleClearForm() {
// Logic for resetting the form
}
handleInput(e) {
let self=this
let value = e.target.value;
console.log("value:" + value);
let name = e.target.name;
//update State
this.setValue(self.state.newCoop,name,value)
}
setValue = (obj,is, value) => {
if (typeof is == 'string')
return this.setValue(obj,is.split('.'), value);
else if (is.length === 1 && value!==undefined)
return this.setState({obj: obj[is[0]] = value});
else if (is.length === 0)
return obj;
else
return this.setValue(obj[is[0]],is.slice(1), value);
}
render() {
return (
<form className="container-fluid" onSubmit={this.handleFormSubmit}>
<FormGroup
controlId="formBasicText">
<Input inputType={'text'}
title= {'Name'}
name= {'name'}
value={this.state.newCoop.name}
placeholder = {'Enter cooperative name'}
handleChange = {this.handleInput}
/> {/* Name of the cooperative */}
<Input inputType={'text'}
title= {'Type'}
name= {'type.name'}
value={this.state.newCoop.type.name}
placeholder = {'Enter cooperative type'}
handleChange = {this.handleInput}
/> {/* Type of the cooperative */}
<Input inputType={'text'}
title= {'Street'}
name= {'address.formatted'}
value={this.state.newCoop.address.formatted}
placeholder = {'Enter address street'}
handleChange = {this.handleInput}
/> {/* Address street of the cooperative */}
<Input inputType={'text'}
title= {'City'}
name= {'address.locality.name'}
value={this.state.newCoop.address.locality.name}
placeholder = {'Enter address city'}
handleChange = {this.handleInput}
/> {/* Address city of the cooperative */}
<Country title={'Country'}
name={'address.country'}
options = {this.state.countries}
value = {this.state.newCoop.address.country}
placeholder = {'Select Country'}
handleChange = {this.handleInput}
/> {/* Country Selection */}
<Province title={'State'}
name={'address.locality.state'}
options = {this.state.provinces}
value = {this.state.newCoop.address.locality.state}
placeholder = {'Select State'}
handleChange = {this.handleInput}
/> {/* State Selection */}
<Input inputType={'text'}
title= {'Postal Code'}
name= {'address.locality.postal_code'}
value={this.state.newCoop.address.locality.postal_code}
placeholder = {'Enter postal code'}
handleChange = {this.handleInput}
/> {/* Address postal code of the cooperative */}
<Input inputType={'text'}
title= {'Email'}
name= {'email'}
value={this.state.newCoop.email}
placeholder = {'Enter email'}
handleChange = {this.handleInput}
/> {/* Email of the cooperative */}
<Input inputType={'text'}
title= {'Phone'}
name= {'phone'}
value={this.state.newCoop.phone}
placeholder = {'Enter phone number'}
handleChange = {this.handleInput}
/> {/* Phone number of the cooperative */}
<Input inputType={'text'}
title= {'Web Site'}
name= {'web_site'}
value={this.state.newCoop.web_site}
placeholder = {'Enter web site'}
handleChange = {this.handleInput}
/> {/* Web site of the cooperative */}
<Button
action = {this.handleFormSubmit}
type = {'primary'}
title = {'Submit'}
style={buttonStyle}
/> { /*Submit */ }
<Button
action = {this.handleClearForm}
type = {'secondary'}
title = {'Clear'}
style={buttonStyle}
/> {/* Clear the form */}
</FormGroup>
</form>
);
}
componentDidMount() {
let initialCountries = [];
let initialProvinces = [];
// Get initial countries
fetch('/countries/')
.then(response => {
return response.json();
}).then(data => {
initialCountries = data.map((country) => {
return country
});
console.log("output ...");
console.log(initialCountries);
this.setState({
countries: initialCountries,
});
});
// Get initial provinces (states)
fetch('/states/484/')
.then(response => {
return response.json();
}).then(data => {
console.log(data);
initialProvinces = data.map((province) => {
return province
});
this.setState({
provinces: initialProvinces,
});
});
}
}
const buttonStyle = {
margin : '10px 10px 10px 10px'
}
export default FormContainer;
当服务器(Django Python 应用程序)无法处理表单时,它会 returns 一个 400 响应正文,错误链接到每个字段。例如,这样的响应主体看起来像
{"phone":["The phone number entered is not valid."]}
捕获此错误并将其显示在我的表单中的正确方法是什么?我见过的所有示例都在提交表单之前处理验证——例如在结束之前在 "handleFormSubmit" 处理程序中编写一个 "validateFields" 方法或类似的东西。
编辑: 添加了新的提取方法以响应评论。但是,当获取请求返回 400 时,添加 "catch" 块不会显示错误。
fetch('/coops/',{
method: "POST",
body: JSON.stringify(this.state.newCoop),
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
}).then(response => {
response.json().then(data =>{
console.log("Successful" + data);
})
}).catch(error => console.log(error))
CHrome 开发控制台报告请求 400 以及
FormContainer.jsx:54 Fetch failed loading: POST "http://localhost:3000/coops/".
此外,尽管有 400 和 Chrome 报告的内容,"Success" console.log 消息仍会打印出来。
正在处理表单提交错误响应。
fetch('/coops/',{
method: "POST",
body: JSON.stringify(this.state.newCoop),
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
}).then(response => {
// check if received an OK response, throw error if not
if (response.ok) {
return response.json();
}
throw new Error(response.statusText);
}).then(jsonData => {
console.log("Successful", jsonData);
}).catch(errors => {
// If error was thrown then unpack here and handle in component/app state
console.log('error', errors);
this.setState({ errors });
});
显示一个简单的错误列表(假设所有错误都是形状 {[string]: arrayOf(string)}
)
render() {
const errors = Object.values(this.state.errors);
return (
<form
className="container-fluid"
onSubmit={this.handleFormSubmit}
>
<FormGroup controlId="formBasicText">
{errors.length && (
<ul>
{errors.map(error => <li>{error.join(" ")}</li>}
</ul>
)}
<Input
inputType={'text'}
title= {'Name'}
name= {'name'}
value={this.state.newCoop.name}
placeholder = {'Enter cooperative name'}
handleChange = {this.handleInput}
/> {/* Name of the cooperative */}
...
显示字段级错误
render() {
const { errors } = this.state;
return (
<form
className="container-fluid"
onSubmit={this.handleFormSubmit}
>
<FormGroup controlId="formBasicText">
<Input
inputType={'text'}
title= {'Name'}
name= {'name'}
value={this.state.newCoop.name}
placeholder = {'Enter cooperative name'}
handleChange = {this.handleInput}
/> {/* Name of the cooperative */}
{errors.name && (
<div className="fieldError">
{error.name.join(" ")}
</div>
)}
<Input
inputType={'text'}
title= {'Type'}
name= {'type.name'}
value={this.state.newCoop.type.name}
placeholder = {'Enter cooperative type'}
handleChange = {this.handleInput}
/> {/* Type of the cooperative */}
{errors.name && (
<div className="fieldError">
{error['type.name"].join(" ")}
</div>
)}
...
后者是一种常见的模式,许多表单库直接将表单错误作为 prop 并自行处理显示错误帮助文本。许多处理两者的形式,使用保留的表单级错误键,即 _error
,然后是所有字段级错误,它们使用字段名作为它们的键。