如何避免以 React 形式添加字段更改处理方法和其他样板文件?
How to avoid adding field change handling methods and other boilerplates in react form?
我有一个基于反应的表单,里面有超过 10 个字段,我有 states 用于那十个 form
字段 (受控组件) .
其中大多数 input
字段仅属于 text
类型,但稍后将添加其他类型的字段。
问题是我需要为它们编写 10 个更改处理程序 以正确设置状态,然后在 constructor[=29 中为每个处理程序添加方法绑定=].
I am quite new to react and may be not aware about correct
methodologies and techniques.
请指导我如何改进我当前的代码结构,避免编写样板代码和重复容易出错的代码。
我当前的注册组件如下 -
export default class Register extends Component {
constructor(props){
super(props);
this.state = {
regName : '',
regAdd1 : '',
regAdd2 : '',
regState : '',
regZipCode : '',
regCity : '',
regPhone : ''
};
// add bindings .... ugh..
this.changeRegAdd1 = this.changeRegAdd1.bind(this);
this.changeRegAdd2 = this.changeRegAdd2.bind(this);
//Similary binding for other handlers...
}
// add individual field change handlers ... ugh...
changeRegName(e) {
this.setState({regName:e.target.value});
}
changeRegAdd1(e) {
this.setState({regAdd1:e.target.value});
}
changeRegAdd2(e) {
this.setState({regAdd2:e.target.value});
}
changeRegState(e) {
this.setState({regState:e.target.value});
}
// Similary for other change handler ....
handleSubmit(e) {
e.preventDefault();
// validate then do other stuff
}
render(){
let registrationComp = (
<div className="row">
<div className="col-md-12">
<h3>Registration Form</h3>
<fieldset>
<div className="form-group">
<div className="col-xs-12">
<label htmlFor="regName">Name</label>
<input type="text" placeholder="Name"
onChange={this.changeregName} value = {this.state.regName} className="form-control" required autofocus/>
</div>
</div>
<div className="form-group">
<div className="col-xs-12">
<label htmlFor="regAdd1">Address Line1</label>
<input
type = "text"
placeholder = "Address Line1"
onChange = {this.changeregAdd1}
value = {this.state.regAdd1}
className = "form-control"
required
autofocus
/>
<input
type = "text"
placeholder = "Address Line2"
onChange = {this.changeregAdd2}
value = {this.state.regAdd2}
className = "form-control"
required
autofocus
/>
</div>
</div>
<div className="form-group">
<div className="col-xs-6">
<label htmlFor="regState">State</label>
<input
type = "text"
placeholder = "State"
onChange = {this.changeregState}
value = {this.state.regState}
className = "form-control"
required
autofocus
/>
</div>
<div className="col-xs-6">
<label htmlFor="regZipCode">Zip Code</label>
<input
type = "text"
placeholder = "Zip Code"
onChange = {this.changeregZipCode}
value = {this.state.regZipCode}
className = "form-control"
required
autofocus
/>
</div>
</div>
<div className="form-group">
<div className="col-xs-12">
<label htmlFor="regCity">City</label>
<input
type = "text"
placeholder = "City"
title = "City"
onChange = {this.changeregCity}
value = {this.state.regCity}
className = "form-control"
required
autofocus
/>
</div>
</div>
{/* other form fields */}
</fieldset>
</div>
</div>
);
return registrationComp;
}
}
还有很多我可能不知道的其他方法。
但我更喜欢在通用方法中对某些类型的通用字段进行更改处理,例如 <input type of text />
这是一个示例输入字段 -
<input
onChange = {this.onChange}
value = {this.state.firstName}
type = "text"
name = {"firstName"}
/>
我保持状态的字段名称和输入的 "name" 属性相同。
之后,我为所有这些字段编写了一个通用更改处理程序。
只需在更改处理程序中写入一行。
onChange(e) {
this.setState({[e.target.name]: e.target.value});
}
我正在使用 es6 的动态 属性 字符串设置作为 属性 名称
{ ["propName] : propValue }.
为了避免为 React 组件中的方法编写手动绑定,您可以遵循以下两种方法 -
- 使用 es6 箭头函数
- 黑客攻击(我不知道这种方法是快还是慢,但它有效:))
在您的组件中创建一个这样的方法。
_bind(...methods) {
methods.forEach( (method) => this[method] = this[method].bind(this) );
}
使用 _bind
绑定您的方法。
constructor(props){
super(props);
this.state = {
regName : '',
regAdd1 : '',
regAdd2 : '',
regState : '',
regZipCode : '',
regCity : '',
regPhone : ''
};
// add bindings .... ugh..
//this.changeRegAdd1 = this.changeRegAdd1.bind(this);
//this.changeRegAdd2 = this.changeRegAdd2.bind(this);
//Similary binding for other handlers...
this._bind(
'changeRegName',
'changeReg1' , 'changeRegAdd2'
// and so on.
);
}
编辑:
添加验证 -
- 编写一个可以遍历状态键并检查状态是否为空的方法。
- 如果任何输入字段为空,请收集有关该输入的详细信息并标记为必填项。
设置一个状态来指示表单有错误。具体的错误详情可以在状态的错误对象中找到。
validateInput() {
let errors = {};
Object.keys(this.state)
.forEach((stateKey) => {
isEmpty(this.state[stateKey]) ? (errors[stateKey] = `*required` ) : null;
});
return {
errors,
isValid : isEmptyObj(errors)
};
}
isFormValid() {
const { errors, isValid } = this.validateInput();
if (!isValid) {
this.setState({ errors});
}
return isValid;
}
onSubmit(e) {
e.preventDefault();
this.setState({errors : {}});
if (this.isFormValid()) {
// Perform form submission
}
}
我已经习惯了调用isEmpty
、isEmptyObj
的实用方法。他们只是检查对象是否为空或未定义或字段是否为空。
希望对您有所帮助。
您可以创建一个更高阶的组件来为您处理很多这样的事情。 Redux Form 有一个很好的模式,如果你愿意,你可以模仿它。
你基本上会得到类似下面的结果(我根本没有测试过这个,但它应该工作得很好):
export class Field extends Component {
handleChange = (event) => this.props.onChange(this.props.name, event.target.value)
render() {
const InputComponent = this.props.component
const value = this.props.value || ''
return (
<InputComponent
{...this.props}
onChange={this.handleChange}
value={value}
/>
}
}
export default function createForm(WrappedComponent) {
class Form extends Component {
constructor() {
super()
this.state = this.props.initialValues || {}
this.handleChange = this.handleChange.bind(this)
}
handleChange(name, value) {
this.setState({
[name]: value,
})
}
render() {
return (
<WrappedComponent
{...this.state}
{...this.props}
// pass anything you want to add here
onChange={this.handleChange}
values={this.state}
/>
)
}
}
return Form
}
然后您可以根据需要扩充这一组件(添加焦点、模糊、提交处理程序等)。你会像这样使用它:
import createForm, { Field } from './createForm'
// simplified Registration component
export class Registration extends Component {
render() {
return (
<form>
<Field
component="input"
name="name"
onChange={this.props.onChange}
type="text"
value={this.props.values.name}
/>
</form>
)
}
}
export default createForm(Registration)
你可能会发疯并进入 context 这样你就不必手动传递值和函数,但至少在你更熟悉 React 之前我会远离它。
此外,如果您不想手动绑定函数,您可以使用 the class properties transform 如果您使用的是 babel。那么 Form
组件将如下所示:
class Form extends Component {
state = this.props.initialValues || {}
handleChange = (name, value) => this.setState({
[name]: value,
})
render() {
return (
<WrappedComponent
{...this.state}
{...this.props}
onChange={this.handleChange}
values={this.state}
/>
)
}
}
看看NeoForm是怎么做的:
- 数据状态直接映射到表单字段
- 一个
onChange
整个表单的处理程序
- 每字段(例如
onBlur
)和表单(例如onSubmit
)验证
- 普通对象和不可变状态助手
- 与 Redux 或任何其他状态管理解决方案轻松集成
它在内部使用 context
,非常小且模块化的源代码很容易理解。
我有一个基于反应的表单,里面有超过 10 个字段,我有 states 用于那十个 form
字段 (受控组件) .
其中大多数 input
字段仅属于 text
类型,但稍后将添加其他类型的字段。
问题是我需要为它们编写 10 个更改处理程序 以正确设置状态,然后在 constructor[=29 中为每个处理程序添加方法绑定=].
I am quite new to react and may be not aware about correct methodologies and techniques.
请指导我如何改进我当前的代码结构,避免编写样板代码和重复容易出错的代码。
我当前的注册组件如下 -
export default class Register extends Component {
constructor(props){
super(props);
this.state = {
regName : '',
regAdd1 : '',
regAdd2 : '',
regState : '',
regZipCode : '',
regCity : '',
regPhone : ''
};
// add bindings .... ugh..
this.changeRegAdd1 = this.changeRegAdd1.bind(this);
this.changeRegAdd2 = this.changeRegAdd2.bind(this);
//Similary binding for other handlers...
}
// add individual field change handlers ... ugh...
changeRegName(e) {
this.setState({regName:e.target.value});
}
changeRegAdd1(e) {
this.setState({regAdd1:e.target.value});
}
changeRegAdd2(e) {
this.setState({regAdd2:e.target.value});
}
changeRegState(e) {
this.setState({regState:e.target.value});
}
// Similary for other change handler ....
handleSubmit(e) {
e.preventDefault();
// validate then do other stuff
}
render(){
let registrationComp = (
<div className="row">
<div className="col-md-12">
<h3>Registration Form</h3>
<fieldset>
<div className="form-group">
<div className="col-xs-12">
<label htmlFor="regName">Name</label>
<input type="text" placeholder="Name"
onChange={this.changeregName} value = {this.state.regName} className="form-control" required autofocus/>
</div>
</div>
<div className="form-group">
<div className="col-xs-12">
<label htmlFor="regAdd1">Address Line1</label>
<input
type = "text"
placeholder = "Address Line1"
onChange = {this.changeregAdd1}
value = {this.state.regAdd1}
className = "form-control"
required
autofocus
/>
<input
type = "text"
placeholder = "Address Line2"
onChange = {this.changeregAdd2}
value = {this.state.regAdd2}
className = "form-control"
required
autofocus
/>
</div>
</div>
<div className="form-group">
<div className="col-xs-6">
<label htmlFor="regState">State</label>
<input
type = "text"
placeholder = "State"
onChange = {this.changeregState}
value = {this.state.regState}
className = "form-control"
required
autofocus
/>
</div>
<div className="col-xs-6">
<label htmlFor="regZipCode">Zip Code</label>
<input
type = "text"
placeholder = "Zip Code"
onChange = {this.changeregZipCode}
value = {this.state.regZipCode}
className = "form-control"
required
autofocus
/>
</div>
</div>
<div className="form-group">
<div className="col-xs-12">
<label htmlFor="regCity">City</label>
<input
type = "text"
placeholder = "City"
title = "City"
onChange = {this.changeregCity}
value = {this.state.regCity}
className = "form-control"
required
autofocus
/>
</div>
</div>
{/* other form fields */}
</fieldset>
</div>
</div>
);
return registrationComp;
}
}
还有很多我可能不知道的其他方法。
但我更喜欢在通用方法中对某些类型的通用字段进行更改处理,例如 <input type of text />
这是一个示例输入字段 -
<input
onChange = {this.onChange}
value = {this.state.firstName}
type = "text"
name = {"firstName"}
/>
我保持状态的字段名称和输入的 "name" 属性相同。 之后,我为所有这些字段编写了一个通用更改处理程序。
只需在更改处理程序中写入一行。
onChange(e) {
this.setState({[e.target.name]: e.target.value});
}
我正在使用 es6 的动态 属性 字符串设置作为 属性 名称
{ ["propName] : propValue }.
为了避免为 React 组件中的方法编写手动绑定,您可以遵循以下两种方法 -
- 使用 es6 箭头函数
- 黑客攻击(我不知道这种方法是快还是慢,但它有效:))
在您的组件中创建一个这样的方法。
_bind(...methods) {
methods.forEach( (method) => this[method] = this[method].bind(this) );
}
使用 _bind
绑定您的方法。
constructor(props){
super(props);
this.state = {
regName : '',
regAdd1 : '',
regAdd2 : '',
regState : '',
regZipCode : '',
regCity : '',
regPhone : ''
};
// add bindings .... ugh..
//this.changeRegAdd1 = this.changeRegAdd1.bind(this);
//this.changeRegAdd2 = this.changeRegAdd2.bind(this);
//Similary binding for other handlers...
this._bind(
'changeRegName',
'changeReg1' , 'changeRegAdd2'
// and so on.
);
}
编辑:
添加验证 -
- 编写一个可以遍历状态键并检查状态是否为空的方法。
- 如果任何输入字段为空,请收集有关该输入的详细信息并标记为必填项。
设置一个状态来指示表单有错误。具体的错误详情可以在状态的错误对象中找到。
validateInput() { let errors = {}; Object.keys(this.state) .forEach((stateKey) => { isEmpty(this.state[stateKey]) ? (errors[stateKey] = `*required` ) : null; }); return { errors, isValid : isEmptyObj(errors) }; } isFormValid() { const { errors, isValid } = this.validateInput(); if (!isValid) { this.setState({ errors}); } return isValid; } onSubmit(e) { e.preventDefault(); this.setState({errors : {}}); if (this.isFormValid()) { // Perform form submission } }
我已经习惯了调用isEmpty
、isEmptyObj
的实用方法。他们只是检查对象是否为空或未定义或字段是否为空。
希望对您有所帮助。
您可以创建一个更高阶的组件来为您处理很多这样的事情。 Redux Form 有一个很好的模式,如果你愿意,你可以模仿它。
你基本上会得到类似下面的结果(我根本没有测试过这个,但它应该工作得很好):
export class Field extends Component {
handleChange = (event) => this.props.onChange(this.props.name, event.target.value)
render() {
const InputComponent = this.props.component
const value = this.props.value || ''
return (
<InputComponent
{...this.props}
onChange={this.handleChange}
value={value}
/>
}
}
export default function createForm(WrappedComponent) {
class Form extends Component {
constructor() {
super()
this.state = this.props.initialValues || {}
this.handleChange = this.handleChange.bind(this)
}
handleChange(name, value) {
this.setState({
[name]: value,
})
}
render() {
return (
<WrappedComponent
{...this.state}
{...this.props}
// pass anything you want to add here
onChange={this.handleChange}
values={this.state}
/>
)
}
}
return Form
}
然后您可以根据需要扩充这一组件(添加焦点、模糊、提交处理程序等)。你会像这样使用它:
import createForm, { Field } from './createForm'
// simplified Registration component
export class Registration extends Component {
render() {
return (
<form>
<Field
component="input"
name="name"
onChange={this.props.onChange}
type="text"
value={this.props.values.name}
/>
</form>
)
}
}
export default createForm(Registration)
你可能会发疯并进入 context 这样你就不必手动传递值和函数,但至少在你更熟悉 React 之前我会远离它。
此外,如果您不想手动绑定函数,您可以使用 the class properties transform 如果您使用的是 babel。那么 Form
组件将如下所示:
class Form extends Component {
state = this.props.initialValues || {}
handleChange = (name, value) => this.setState({
[name]: value,
})
render() {
return (
<WrappedComponent
{...this.state}
{...this.props}
onChange={this.handleChange}
values={this.state}
/>
)
}
}
看看NeoForm是怎么做的:
- 数据状态直接映射到表单字段
- 一个
onChange
整个表单的处理程序 - 每字段(例如
onBlur
)和表单(例如onSubmit
)验证 - 普通对象和不可变状态助手
- 与 Redux 或任何其他状态管理解决方案轻松集成
它在内部使用 context
,非常小且模块化的源代码很容易理解。