Я создал форму для нескольких входов, где конкретные входные данные должны быть проверены во время ввода данных и еще раз для всех данных непосредственно перед отправкой формы на бэкэнд. Условия подачи: все поля обязательны, данные действительны.
Моя программа работает, но мне не нравится, что я повторяю код проверки в 2-х местах: в ErrorOutput и hadleSubmit.
В ErrorOutput я проверяю данные и, при необходимости, показываю сообщение об ошибке.
В handleSubmit я просто проверяю данные без отображения сообщения об ошибке, и если все данные действительны, я подтверждаю отправку.
Как я могу улучшить свой пример, чтобы предотвратить повторение этого кода, но проверка данных также проводилась во время ввода данных и перед отправкой?
import React from 'react'
import { render } from 'react-dom'
const ErrorOutput = props => {
let name = props.name
let inputValue = props.case
let submit = props.submit
// Data validation
if (name === 'firstName') {
if (!inputValue.match(/^[a-zA-Z]+$/) && inputValue.length > 0) {
return <span>Letters only</span>
} else if (submit && inputValue.length === 0) {
return <span>Required</span>
}
return <span></span>
}
if (name === 'telNo') {
if (!inputValue.match(/^[0-9]+$/) && inputValue.length > 0) {
return <span>Numbers only</span>
} else if (submit && inputValue.length === 0) {
return <span>Required</span>
}
return <span></span>
}
}
class App extends React.Component {
constructor(props){
super(props)
this.state = {
firstName: '',
telNo: '',
submit: false
}
}
handleSubmit(e){
e.preventDefault()
let submit = true
let error = true
const { firstName, telNo } = this.state
this.setState ({submit: submit})
// Repeat the data validation before submission
if (firstName === '' || !firstName.match(/^[a-zA-Z]+$/)) {
error = true
} else if (telNo === '' || !telNo.match(/^[0-9]+$/)) {
error = true
} else {
error = false
}
// Submited if all data is valid
if (!error) {
// send data
return alert('Success!')
}
}
handleValidation(e) {
this.setState({
[e.target.name]: e.target.value
})
}
render() {
return (
<form onSubmit = {this.handleSubmit.bind(this)}>
<div>
<label>
First name:
</label>
<input
type='text'
name ='firstName'
value = {this.state.firstName}
onChange = {this.handleValidation.bind(this)}
/>
<ErrorOutput case = {this.state.firstName} name = {'firstName'} submit = {this.state.submit} />
</div>
<div>
<label>
Phone number:
</label>
<input
type='tel'
name ='telNo'
value = {this.state.telNo}
onChange = {this.handleValidation.bind(this)}
/>
<ErrorOutput case = {this.state.telNo} name = {'telNo'} submit = {this.state.submit} />
</div>
<button>
Submit
</button>
</form>
)
}
}
render(
<App />,
document.getElementById('root')
)




Вы можете извлечь компонент FormItem:
class FormItem extends React.Component {
render() {
return (
<div>
<label>
{this.props.label}
</label>
<input
{...this.props.input}
/>
<ErrorOutput
case = {this.props.input.value}
name = {this.props.input.name}
submit = {this.props.onSubmit}
/>
</div>
);
}
}
и используйте его в своем App:
render() {
return (
<form onSubmit = {this.handleSubmit.bind(this)}>
<FormItem label='First name:' input = {{
type: 'text'
name: 'firstName'
value: this.state.firstName,
onChange: this.handleValidation.bind(this)
}}
onSubmit = {this.state.submit}
/>
<FormItem label='Phone number:' input = {{
type:'tel'
name :'telNo'
value : {this.state.telNo}
onChange : {this.handleValidation.bind(this)}
}}
onSubmit = {this.state.submit}
/>
<button>
Submit
</button>
</form>
)
}
здесь становятся удобными такие библиотеки, как окончательная форма реакции и редукционная форма.
Компонент ErrorOutput не должен ничего проверять, это не входит в обязанности компонента. Вместо этого вы можете проверить свои значения в событии размытия входов и перед отправкой:
class App extends React.Component {
constructor(props){
super(props)
this.state = {
firstName: '',
telNo: '',
submit: false,
errors: {},
invalid: false,
}
}
handleSubmit(e){
e.preventDefault()
if (this.validate()) {
// handle error
} else {
// submit
}
}
validate = () => {
const { firstName, telNo } = this.state
const errors = {}
let invalid = false;
if (firstName === '' || !firstName.match(/^[a-zA-Z]+$/)) {
errors.firstName = 'first name is required'
invalid = true;
} else if (telNo === '' || !telNo.match(/^[0-9]+$/)) {
telNo.telNo = 'telNo is required'
invalid = true;
}
this.setState({
invalid,
errors,
})
return invalid;
}
render() {
return (
<form onSubmit = {this.handleSubmit.bind(this)}>
<FormItem label='First name:' input = {{
type: 'text',
name: 'firstName',
value: this.state.firstName,
onChange: e => this.setState({ firstName: e.target.value }),
onBlur: () => this.validate(),
}}
/>
<FormItem label='Phone number:' input = {{
type: 'tel',
name: 'telNo',
value: this.state.telNo,
onChange: e => this.setState({ telNo: e.target.value }),
onBlur: () => this.validate(),
}}
/>
<button>
Submit
</button>
</form>
)
}
}
и FormItem и ErrorOutput:
const ErrorOutput = ({ error }) => <span>{error}</span>
class FormItem extends React.Component {
render() {
return (
<div>
<label>
{this.props.label}
</label>
<input
{...this.props.input}
/>
{this.props.error && <ErrorOutput error = {this.props.error} />}
</div>
);
}
}
@Herasimenak Я обновил ответ, пожалуйста, посмотрите
const ErrorOutput = ({ errorText }) => <span>{errorText}</span>;
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
firstName: "",
telNo: "",
submit: false,
errors: {} //Add errors object to the state.
};
}
handleSubmit(e) {
e.preventDefault();
const errors = this.validateData();
if (Object.keys(errors).length === 0) {
alert("Success");
}
//else errors exist
this.setState({ errors });
}
validateData = () => {
let errors = {};
const { firstName, telNo } = this.state; // read the values to validate
if (firstName.length === 0) {
errors.firstName = "Required";
} else if (firstName.length > 0 && !firstName.match(/^[a-zA-Z]+$/)) {
errors.firstName = "Letters only";
}
if (telNo.length === 0) {
errors.telNo = "Required";
} else if (telNo.length > 0 && !telNo.match(/^[0-9]+$/)) {
errors.telNo = "Numbers only";
}
return errors;
};
handleValidation(e) {
this.setState({
[e.target.name]: e.target.value
});
}
render() {
const { errors } = this.state; // read errors from the state
return (
<form onSubmit = {this.handleSubmit.bind(this)}>
<div>
<label>First name:</label>
<input
type = "text"
name = "firstName"
value = {this.state.firstName}
onChange = {this.handleValidation.bind(this)}
/>
{errors.firstName && <ErrorOutput errorText = {errors.firstName} />}
</div>
<div>
<label>Phone number:</label>
<input
type = "tel"
name = "telNo"
value = {this.state.telNo}
onChange = {this.handleValidation.bind(this)}
/>
{errors.telNo && <ErrorOutput errorText = {errors.telNo} />}
</div>
<button>Submit</button>
</form>
);
}
}
render(<App />, document.getElementById("root"));
В вашем примере данные проверяются только после того, как вы нажмете кнопку «отправить», но сообщение об ошибке не отображается, когда вы вводите данные.
@EvgenyTimoshenko Я думаю, поскольку код проверки данных находится только в функции validateData, это лучше, чем помещать его в компонент handleSubmit и ErrorOutput.
@Herasimenak Я понимаю, возможно, нам также следует проверять данные, когда событие onChange возникает в элементе ввода, но я не знаю, жизнеспособно ли это.
Спасибо, я воспользуюсь вашим примером для извлечения компонентов. Это действительно полезно. Но мой вопрос о предотвращении повторения кода с правилами проверки все еще открыт. Как я могу решить эту проблему?