如何使用 React.JS 正确验证输入值?
Posted
技术标签:
【中文标题】如何使用 React.JS 正确验证输入值?【英文标题】:How to properly validate input values with React.JS? 【发布时间】:2014-07-24 00:58:26 【问题描述】:我有一个简单的表格。所有组件和状态都保存在 Page 组件中。有 2 个显示标题和 3 个输入字段。第一个输入应该是文本,第二个和第三个应该是整数。当用户输入错误类型的数据时,我希望在输入字段旁边弹出一条错误消息。我的问题与 React.JS 中的最佳实践有关
谁决定该值是否有效?我想输入字段的唯一工作就是将值引导回持有状态的组件,那么这是否意味着只有 Page 才能确定值是否有效?
然后我应该如何让弹出窗口出现? Page 是否必须触发一个新的布尔状态元素,该元素将通过 perp 传递,告诉 Adaptive_Input 显示错误消息?
JSFiddle
JS:
/**
* @jsx React.DOM
*/
var Adaptive_Input = React.createClass(
handle_change: function()
var new_text = this.refs.input.getDOMNode().value;
this.props.on_Input_Change(new_text);
,
render: function()
return (
<div className='adaptive_placeholder_input_container'>
<input
className="adaptive_input"
type="text"
required="required"
onChange= this.handle_change
ref="input"
></input>
<label
className="adaptive_placeholder"
alt=this.props.initial
placeholder=this.props.focused
></label>
</div>
);
);
var Form = React.createClass(
render: function()
return (
<form>
<Adaptive_Input
initial='Name Input'
focused='Name Input'
on_Input_Change=this.props.handle_text_input
/>
<Adaptive_Input
initial='Value 1'
focused='Value 1'
on_Input_Change=this.props.handle_value_1_input
/>
<Adaptive_Input
initial='Value 2'
focused='Value 2'
on_Input_Change=this.props.handle_value_2_input
/>
</form>
);
);
var Page = React.createClass(
getInitialState: function()
return
Name : "No Name",
Value_1 : '0',
Value_2 : '0',
Display_Value: '0'
;
,
handle_text_input: function(new_text)
this.setState(
Name: new_text
);
,
handle_value_1_input: function(new_value)
console.log("===");
var updated_display = parseInt(new_value) + parseInt(this.state.Value_2);
updated_display = updated_display.toString();
this.setState(
Display_Value: updated_display
);
,
handle_value_2_input: function(new_value)
var updated_display = parseInt(this.state.Value_1) + parseInt(new_value);
updated_display = updated_display.toString();
this.setState(
Display_Value: updated_display
);
,
render: function()
return(
<div>
<h2>this.state.Name</h2>
<h2>Value 1 + Value 2 = this.state.Display_Value</h2>
<Form
handle_text_input=this.handle_text_input
handle_value_1_input = this.handle_value_1_input
handle_value_2_input = this.handle_value_2_input
/>
</div>
);
);
React.renderComponent(<Page />, document.body);
【问题讨论】:
这是一本好书? christianalfoni.github.io/javascript/2014/10/22/… 如果使用 Node,一个简单的解决方案是使用来自 npm 的react-validation。 【参考方案1】:有时您的应用程序中可以有多个具有类似验证的字段。在这种情况下,我建议您创建公共组件字段来保存此验证。
例如,假设您在应用程序的几个位置强制输入文本。您可以创建一个 TextInput 组件:
constructor(props)
super(props);
this.state =
touched: false, error: '', class: '', value: ''
onValueChanged = (event) =>
let [error, validClass, value] = ["", "", event.target.value];
[error, validClass] = (!value && this.props.required) ?
["Value cannot be empty", "is-invalid"] : ["", "is-valid"]
this.props.onChange(value: value, error: error);
this.setState(
touched: true,
error: error,
class: validClass,
value: value
)
render()
return (
<div>
<input type="text"
value=this.props.value
onChange=this.onValueChanged
className="form-control " + this.state.class
id="this.props.id"
placeholder=this.props.placeholder />
this.state.error ?
<div className="invalid-feedback">
this.state.error
</div> : null
</div>
)
然后您就可以在应用程序的任何地方使用这样的组件:
constructor(props)
super(props);
this.state =
user: firstName: '', lastName: '',
formState:
firstName: error: '' ,
lastName: error: ''
onFirstNameChange = (model) =>
let user = this.state.user;
user.firstName = model.value;
this.setState(
user: user,
formState: ...this.state.formState, firstName: error: model.error
)
onLastNameChange = (model) =>
let user = this.state.user;
user.lastName = model.value;
this.setState(
user: user,
formState: ...this.state.formState, lastName: error: model.error
)
onSubmit = (e) =>
// submit logic
render()
return (
<form onSubmit=this.onSubmit>
<TextInput id="input_firstName"
value=this.state.user.firstName
onChange=this.onFirstNameChange
required = true
placeholder="First name" />
<TextInput id="input_lastName"
value=this.state.user.lastName
onChange=this.onLastNameChange
required = true
placeholder="Last name" />
this.state.formState.firstName.error || this.state.formState.lastName.error ?
<button type="submit" disabled className="btn btn-primary margin-left disabled">Save</button>
: <button type="submit" className="btn btn-primary margin-left">Save</button>
</form>
)
好处:
您无需重复验证逻辑 表单中的代码更少 - 更具可读性 其他常用的输入逻辑可以保留在组件中 您遵循 React 规则,即组件应尽可能愚蠢参考。 https://webfellas.tech/#/article/5
【讨论】:
【参考方案2】:我过去使用过 redux-form 和 formik,最近 React 引入了 Hook,我已经为它构建了一个自定义的 hook。请检查一下,看看它是否使您的表单验证更容易。
Github:https://github.com/bluebill1049/react-hook-form
网址:http://react-hook-form.now.sh
使用这种方法,您也不再进行受控输入。
下面的例子:
import React from 'react'
import useForm from 'react-hook-form'
function App()
const register, handleSubmit, errors = useForm() // initialise the hook
const onSubmit = (data) => console.log(data) // callback when validation pass
return (
<form onSubmit=handleSubmit(onSubmit)>
<input name="firstname" ref=register /> /* register an input */
<input name="lastname" ref=register( required: true ) /> /* apply required validation */
errors.lastname && 'Last name is required.' /* error message */
<input name="age" ref=register( pattern: /\d+/ ) /> /* apply a Refex validation */
errors.age && 'Please enter number for age.' /* error message */
<input type="submit" />
</form>
)
【讨论】:
【参考方案3】:在输入文本字段及其下方使用 onChange=this.handleChange.bind(this, "name")
方法和 value=this.state.fields["name"]
创建 span 元素以显示错误,请参见下面的示例。
export default class Form extends Component
constructor()
super()
this.state =
fields:
name:'',
email: '',
message: ''
,
errors: ,
disabled : false
handleValidation()
let fields = this.state.fields;
let errors = ;
let formIsValid = true;
if(!fields["name"])
formIsValid = false;
errors["name"] = "Name field cannot be empty";
if(typeof fields["name"] !== "undefined" && !fields["name"] === false)
if(!fields["name"].match(/^[a-zA-Z]+$/))
formIsValid = false;
errors["name"] = "Only letters";
if(!fields["email"])
formIsValid = false;
errors["email"] = "Email field cannot be empty";
if(typeof fields["email"] !== "undefined" && !fields["email"] === false)
let lastAtPos = fields["email"].lastIndexOf('@');
let lastDotPos = fields["email"].lastIndexOf('.');
if (!(lastAtPos < lastDotPos && lastAtPos > 0 && fields["email"].indexOf('@@') === -1 && lastDotPos > 2 && (fields["email"].length - lastDotPos) > 2))
formIsValid = false;
errors["email"] = "Email is not valid";
if(!fields["message"])
formIsValid = false;
errors["message"] = " Message field cannot be empty";
this.setState(errors: errors);
return formIsValid;
handleChange(field, e)
let fields = this.state.fields;
fields[field] = e.target.value;
this.setState(fields);
handleSubmit(e)
e.preventDefault();
if(this.handleValidation())
console.log('validation successful')
else
console.log('validation failed')
render()
return (
<form onSubmit=this.handleSubmit.bind(this) method="POST">
<div className="row">
<div className="col-25">
<label htmlFor="name">Name</label>
</div>
<div className="col-75">
<input type="text" placeholder="Enter Name" refs="name" onChange=this.handleChange.bind(this, "name") value=this.state.fields["name"]/>
<span style=color: "red">this.state.errors["name"]</span>
</div>
</div>
<div className="row">
<div className="col-25">
<label htmlFor="exampleInputEmail1">Email address</label>
</div>
<div className="col-75">
<input type="email" placeholder="Enter Email" refs="email" aria-describedby="emailHelp" onChange=this.handleChange.bind(this, "email") value=this.state.fields["email"]/>
<span style=color: "red">this.state.errors["email"]</span>
</div>
</div>
<div className="row">
<div className="col-25">
<label htmlFor="message">Message</label>
</div>
<div className="col-75">
<textarea type="text" placeholder="Enter Message" rows="5" refs="message" onChange=this.handleChange.bind(this, "message") value=this.state.fields["message"]></textarea>
<span style=color: "red">this.state.errors["message"]</span>
</div>
</div>
<div className="row">
<button type="submit" disabled=this.state.disabled>this.state.disabled ? 'Sending...' : 'Send'</button>
</div>
</form>
)
【讨论】:
【参考方案4】:你的 jsfiddle 不再工作了。 我已经修复它:http://jsfiddle.net/tkrotoff/bgC6E/40/ 使用 React 16 和 ES6 类。
class Adaptive_Input extends React.Component
handle_change(e)
var new_text = e.currentTarget.value;
this.props.on_Input_Change(new_text);
render()
return (
<div className="adaptive_placeholder_input_container">
<input
className="adaptive_input"
type="text"
required="required"
onChange=this.handle_change.bind(this) />
<label
className="adaptive_placeholder"
alt=this.props.initial
placeholder=this.props.focused />
</div>
);
class Form extends React.Component
render()
return (
<form>
<Adaptive_Input
initial='Name Input'
focused='Name Input'
on_Input_Change=this.props.handle_text_input />
<Adaptive_Input
initial='Value 1'
focused='Value 1'
on_Input_Change=this.props.handle_value_1_input />
<Adaptive_Input
initial='Value 2'
focused='Value 2'
on_Input_Change=this.props.handle_value_2_input />
</form>
);
class Page extends React.Component
constructor(props)
super(props);
this.state =
Name: 'No Name',
Value_1: '0',
Value_2: '0',
Display_Value: '0'
;
handle_text_input(new_text)
this.setState(
Name: new_text
);
handle_value_1_input(new_value)
new_value = parseInt(new_value);
var updated_display = new_value + parseInt(this.state.Value_2);
updated_display = updated_display.toString();
this.setState(
Value_1: new_value,
Display_Value: updated_display
);
handle_value_2_input(new_value)
new_value = parseInt(new_value);
var updated_display = parseInt(this.state.Value_1) + new_value;
updated_display = updated_display.toString();
this.setState(
Value_2: new_value,
Display_Value: updated_display
);
render()
return(
<div>
<h2>this.state.Name</h2>
<h2>Value 1 + Value 2 = this.state.Display_Value</h2>
<Form
handle_text_input=this.handle_text_input.bind(this)
handle_value_1_input=this.handle_value_1_input.bind(this)
handle_value_2_input=this.handle_value_2_input.bind(this)
/>
</div>
);
ReactDOM.render(<Page />, document.getElementById('app'));
由于这个库,现在相同的代码被表单验证破解了:https://github.com/tkrotoff/react-form-with-constraints => http://jsfiddle.net/tkrotoff/k4qa4heg/
const FormWithConstraints, FieldFeedbacks, FieldFeedback = ReactFormWithConstraints;
class Adaptive_Input extends React.Component
static contextTypes =
form: PropTypes.object.isRequired
;
constructor(props)
super(props);
this.state =
field: undefined
;
this.fieldWillValidate = this.fieldWillValidate.bind(this);
this.fieldDidValidate = this.fieldDidValidate.bind(this);
componentWillMount()
this.context.form.addFieldWillValidateEventListener(this.fieldWillValidate);
this.context.form.addFieldDidValidateEventListener(this.fieldDidValidate);
componentWillUnmount()
this.context.form.removeFieldWillValidateEventListener(this.fieldWillValidate);
this.context.form.removeFieldDidValidateEventListener(this.fieldDidValidate);
fieldWillValidate(fieldName)
if (fieldName === this.props.name) this.setState(field: undefined);
fieldDidValidate(field)
if (field.name === this.props.name) this.setState(field);
handle_change(e)
var new_text = e.currentTarget.value;
this.props.on_Input_Change(e, new_text);
render()
const field = this.state;
let className = 'adaptive_placeholder_input_container';
if (field !== undefined)
if (field.hasErrors()) className += ' error';
if (field.hasWarnings()) className += ' warning';
return (
<div className=className>
<input
type=this.props.type
name=this.props.name
className="adaptive_input"
required
onChange=this.handle_change.bind(this) />
<label
className="adaptive_placeholder"
alt=this.props.initial
placeholder=this.props.focused />
</div>
);
class Form extends React.Component
constructor(props)
super(props);
this.state =
Name: 'No Name',
Value_1: '0',
Value_2: '0',
Display_Value: '0'
;
handle_text_input(e, new_text)
this.form.validateFields(e.currentTarget);
this.setState(
Name: new_text
);
handle_value_1_input(e, new_value)
this.form.validateFields(e.currentTarget);
if (this.form.isValid())
new_value = parseInt(new_value);
var updated_display = new_value + parseInt(this.state.Value_2);
updated_display = updated_display.toString();
this.setState(
Value_1: new_value,
Display_Value: updated_display
);
else
this.setState(
Display_Value: 'Error'
);
handle_value_2_input(e, new_value)
this.form.validateFields(e.currentTarget);
if (this.form.isValid())
new_value = parseInt(new_value);
var updated_display = parseInt(this.state.Value_1) + new_value;
updated_display = updated_display.toString();
this.setState(
Value_2: new_value,
Display_Value: updated_display
);
else
this.setState(
Display_Value: 'Error'
);
render()
return(
<div>
<h2>Name: this.state.Name</h2>
<h2>Value 1 + Value 2 = this.state.Display_Value</h2>
<FormWithConstraints ref=form => this.form = form noValidate>
<Adaptive_Input
type="text"
name="name_input"
initial='Name Input'
focused='Name Input'
on_Input_Change=this.handle_text_input.bind(this) />
<FieldFeedbacks for="name_input">
<FieldFeedback when="*" error />
<FieldFeedback when=value => !/^\w+$/.test(value) warning>Should only contain alphanumeric characters</FieldFeedback>
</FieldFeedbacks>
<Adaptive_Input
type="number"
name="value_1_input"
initial='Value 1'
focused='Value 1'
on_Input_Change=this.handle_value_1_input.bind(this) />
<FieldFeedbacks for="value_1_input">
<FieldFeedback when="*" />
</FieldFeedbacks>
<Adaptive_Input
type="number"
name="value_2_input"
initial='Value 2'
focused='Value 2'
on_Input_Change=this.handle_value_2_input.bind(this) />
<FieldFeedbacks for="value_2_input">
<FieldFeedback when="*" />
</FieldFeedbacks>
</FormWithConstraints>
</div>
);
ReactDOM.render(<Form />, document.getElementById('app'));
这里提出的解决方案是骇人听闻的,因为我试图让它接近原始的 jsfiddle。要使用 react-form-with-constraints 进行正确的表单验证,请查看https://github.com/tkrotoff/react-form-with-constraints#examples
【讨论】:
【参考方案5】:又一次解决同样的问题 - form-container
on npm
【讨论】:
【参考方案6】:我最近花了一周时间研究了很多解决方案来验证我在应用中的表单。我从所有最受瞩目的人开始,但我找不到按预期工作的人。几天后,我变得非常沮丧,直到我发现了一个非常新的和惊人的插件: https://github.com/kettanaito/react-advanced-form
开发人员反应迅速,经过我的研究,他的解决方案值得成为我认为最受关注的解决方案。我希望它可以帮助你,你会感激的。
【讨论】:
【参考方案7】:我写了This library,它允许您包装表单元素组件,并允许您以以下格式定义验证器:-
<Validation group="myGroup1"
validators=[
validator: (val) => !validator.isEmpty(val),
errorMessage: "Cannot be left empty"
,...
]>
<TextField value=this.state.value
className=styles.inputStyles
onChange=
(evt)=>
console.log("you have typed: ", evt.target.value);
/>
</Validation>
【讨论】:
【参考方案8】:您可以使用npm install --save redux-form
我正在编写一个简单的电子邮件和提交按钮表单,用于验证电子邮件并提交表单。使用 redux-form,表单默认在 html onSubmit 操作上运行 event.preventDefault()。
import React, Component from 'react';
import reduxForm from 'redux-form';
class LoginForm extends Component
onSubmit(props)
//do your submit stuff
render()
const fields: email, handleSubmit = this.props;
return (
<form onSubmit=handleSubmit(this.onSubmit.bind(this))>
<input type="text" placeholder="Email"
className=`form-control $email.touched && email.invalid ? 'has-error' : '' `
...email
/>
<span className="text-help">
email.touched ? email.error : ''
</span>
<input type="submit"/>
</form>
);
function validation(values)
const errors = ;
const emailPattern = /(.+)@(.+)2,\.(.+)2,/;
if (!emailPattern.test(values.email))
errors.email = 'Enter a valid email';
return errors;
LoginForm = reduxForm(
form: 'LoginForm',
fields: ['email'],
validate: validation
, null, null)(LoginForm);
export default LoginForm;
【讨论】:
那又怎样?人们仍然会提出这个问题,如果有解决问题的新方法,为什么不呢? Redux 与否 - 它完全有效。【参考方案9】:首先,我将在下面提到一个示例:http://jsbin.com/rixido/2/edit
如何使用 React.JS 正确验证输入值?
随心所欲。 React 用于渲染数据模型。数据模型应该知道什么是有效的。您可以使用 Backbone 模型、JSON 数据或任何您想要表示数据及其错误状态的东西。
更具体地说:
React 通常与您的数据无关。它用于渲染和处理事件。
遵循的规则是:
-
元素可以改变它们的状态。
他们不能改变道具。
他们可以调用回调来更改***道具。
如何决定某个东西应该是道具还是状态?考虑一下:除了文本字段之外,您的应用程序的任何部分都想知道输入的值是错误的吗?如果不是,则使其成为一个状态。如果是,它应该是一个道具。
例如,如果您想要一个单独的视图来呈现“您在此页面上有 2 个错误”。那么***数据模型必须知道您的错误。
该错误应该出现在哪里? 如果您的应用程序正在渲染 Backbone 模型(例如),则模型本身将具有您可以使用的 validate() 方法和 validateError 属性。您可以渲染其他可以执行相同操作的智能对象。 React 还说尽量减少 props 并生成其余数据。因此,如果您有一个验证器(例如 https://github.com/flatiron/revalidator),那么您的验证可能会逐渐减少,任何组件都可以使用匹配的验证检查 props 以查看它是否有效。
这在很大程度上取决于您。
(我个人使用 Backbone 模型并在 React 中渲染它们。我有一个***错误警报,我会在任何地方显示错误,并描述错误。)
【讨论】:
'React 还说尽量减少 props' - 你知道 React 在哪里记录这个吗? “找出应用程序状态的绝对最小表示需要是什么,并按需计算您需要的所有其他内容。” facebook.github.io/react/docs/thinking-in-react.html 第三步 不应该是“React 也说尽量将状态保持在最低限度”吗? @MosesLee 使用道具而不是组件状态将每个组件的状态保持在最低限度。在渲染调用中计算派生值而不是存储它们,将道具(和状态)保持在最低限度。我们试图在两种意义上都将 state 保持在最低限度,在第二种意义上将 props 保持在最低限度。以上是关于如何使用 React.JS 正确验证输入值?的主要内容,如果未能解决你的问题,请参考以下文章
用ant-design在react js中提交后清除表单输入字段值