如何实现Antdesign表单组件封装?⽬标:⾃⼰实现⼀个antd表单组件
先看下Ant Design官⽹上给出的表单组件⽤法:
1 import React, { Component } from 'react'
2 import { Form, Icon, Input, Button } from 'antd'
3
4function hasErrors(fieldsError) {
5return Object.keys(fieldsError).some(field => fieldsError[field])
6 }
7
8 class HorizontalLoginForm extends React.Component {
9 componentDidMount() {
10// To disabled submit button at the beginning.
11this.props.form.validateFields()
12 }
13
14 handleSubmit = e => {
15 e.preventDefault()
16this.props.form.validateFields((err, values) => {
17if (!err) {
18 console.log('Received values of form: ', values)
19 }
20 })
21 };
22
23 render() {
24 const {
25 getFieldDecorator,
26 getFieldsError,
27 getFieldError,
28 isFieldTouched
29 } = this.props.form
30
31// Only show error after a field is touched.
32 const userNameError =
33 isFieldTouched('userName') && getFieldError('userName')
34 const passwordError =
35 isFieldTouched('password') && getFieldError('password')
36return (
37 <Form layout='inline' onSubmit={this.handleSubmit}>
38 <Form.Item
39 validateStatus={userNameError ? 'error' : ''}
40 help={userNameError || ''}
41 >
42 {getFieldDecorator('userName', {
43 rules: [{ required: true, message: 'Please input your username!' }]
44 })(
45 <Input
46 prefix={<Icon type='user' style={{ color: 'rgba(0,0,0,.25)' }} />}
47 placeholder='Username'
48 />
49 )}
50 </Form.Item>
51 <Form.Item
52 validateStatus={passwordError ? 'error' : ''}
53 help={passwordError || ''}
54 >
55 {getFieldDecorator('password', {
56 rules: [{ required: true, message: 'Please input your Password!' }]
57 })(
58 <Input
59 prefix={<Icon type='lock' style={{ color: 'rgba(0,0,0,.25)' }} />}
60 type='password'
61 placeholder='Password'
62 />
63 )}
64 </Form.Item>
65 <Form.Item>
66 <Button
67 type='primary'
68 htmlType='submit'
69 disabled={hasErrors(getFieldsError())}
70 >
71 Log in
72 </Button>
73 </Form.Item>
74 </Form>
75 )
react组件之间通信
76 }
77 }
78
79 const WrappedHorizontalLoginForm = ate({ name: 'horizontal_login' })(
80 HorizontalLoginForm
81 )
82
83 export default WrappedHorizontalLoginForm
组件功能分析:
1-每个input输⼊框被触发后开始做⾮空校验并提⽰错误
2-表单提交时做表单项校验,全部校验成功则提⽰登录,否则提⽰校验失败
3-表单项增加前置图标
组件封装思路:
1-需要⼀个⾼阶函数hoc FormCreate,⽤来包装⽤户表单,增加数据管理能⼒;hoc需要扩展四个功能:getFieldDecorator, getFieldsError, getFieldError, isFieldTouched。获取字段包装器⽅法getFieldDecorator的返回值是个⾼阶函数,接收⼀个Input组件作为参数,返回⼀个新的组件。这就是让⼀个普通的表单项,变成了带有扩展功能的表单项(例如:增加该项的校验规则)
2-FormItem组件,负责校验及错误信息的展⽰,需要保存两个属性,校验状态和错误信息,当前校验通过时错误信息为空
3-Input组件,展⽰型组件,增加输⼊框前置icon
4-导出FormCreate装饰后的MForm组件,MForm组件负责样式布局以及提交控制
组件封装步骤:
1-完成⼀个基础的组件MForm,让页⾯先展⽰出来
2-写⼀个⾼阶组件FormCreate对MForm进⾏扩充,使MForm组件拥有数据管理的能⼒。
保存字段选项设置 this.options = {}; 这⾥不需要保存为state,因为我们不希望字段选项变化⽽让组件重新渲染
保存各字段的值 this.state = {}
定义⽅法 getFieldDecorator()(),第⼀个参数传递配置项,第⼆个参数传⼊Input组件;第⼀个参数包括:当前校验项、校验规则 'username',{rules:[require:true,message:'请输⼊⽤户名']}在FormCreate中,克隆⼀份Input组件,并且定义Input的onChange事件。⾸先这⾥需要把已经存在的jsx克隆⼀份,并修改它的属性,直接修改属性是不允许的;这⾥在更⾼级别定义onChange事件,控制元素的值,这样当组件发⽣变化时,就不⽤进⾏组件之间的来回通信。数据变化交给容器型组件去做,低层级的组件只负责展⽰即可。
3-增加提交校验功能
4-增加FormItem组件,在表单项触发后做实时校验并提⽰错误信息
代码:MForm.js
以下每⼀步骤都可以独⽴运⾏
step1 - 搭建基础代码
1 import React, { Component } from 'react'
2
3 class MForm extends Component {
4 render() {
5return (
6 <div>
7⽤户名:<input type='text' />
8密码:<input type='password' />
9 <button>Log in</button>
10 </div>
11 )
12 }
13 }
14
15 export default MForm
step2 - ⽤⾼阶组件FormCreate对最后导出的MForm组件进⾏能⼒扩充;通过表单项组件FormItem展⽰校验错误信息
1 import React, { Component } from 'react'
2
3// hoc: 包装⽤户表单,增加数据管理能⼒及校验功能
4 const FormCreate = Comp => {
5return class extends Component {
6 constructor(props) {
7 super(props)
8this.options = {} // 保存字段选项设置
9this.state = {} // 保存各字段的值
10 }
11
12// 处理表单项输⼊事件
13 handleChange = e => {
14 const { name, value } = e.target
15this.setState(
16 {
17 [name]: value
18 },
19 () => {
20// TODO: 处理状态变化后的校验
21// 由于setState是异步的,所以这⾥需要在回调函数中处理后续操作
22// 保证状态已经完成改变
23 }
24 )
25 };
26
27 getFieldDecorator = (field, option) => InputComp => {
28this.options[field] = option
29return (
30 <div>
31 {/* 把已经存在的jsx克隆⼀份,并修改它的属性,直接修改属性是不允许的。
32这⾥在更⾼级别定义onChange事件,控制元素的值,这样当组件发⽣变化时,
33就不⽤进⾏组件之间的来回通信 */}
34 {React.cloneElement(InputComp, {
35 name: field, // 控件name
36 value: this.state[field] || '', // 控件值
37 onChange: this.handleChange // change事件处理
38 })}
39 </div>
40 )
41 };
42 render() {
43return (
44 <Comp {...this.props} getFieldDecorator={FieldDecorator} />
45 )
46 }
47 }
48 }
49
50 @FormCreate
51 class MForm extends Component {
52 render() {
53 const { getFieldDecorator } = this.props
54
55return (
56 <div>
57⽤户名:{getFieldDecorator('username', {
58 rules: [{ required: true, message: '请填写⽤户名' }]
59 })(<input type='text' />)}
60密码:{getFieldDecorator('password', {
61 rules: [{ required: true, message: '请填写密码' }]
62 })(<input type='password' />)}
63 <button>Log in</button>
64 </div>
65 )
66 }
67 }
68
69 export default MForm
step3 - 增加点击提交按钮时校验表单项的逻辑
import React, { Component } from 'react'
// hoc: 包装⽤户表单,增加数据管理能⼒及校验功能
const FormCreate = Comp => {
return class extends Component {
constructor(props) {
super(props)
this.options = {} // 保存字段选项设置
this.state = {} // 保存各字段的值
}
// 处理表单项输⼊事件
handleChange = e => {
const { name, value } = e.target
this.setState(
{
[name]: value
},
() => {
// 处理状态变化后的校验
// 由于setState是异步的,所以这⾥需要在回调函数中处理后续操作
// 保证状态已经完成改变
this.validateField(name)
}
)
};
/
/ 表单项校验,可以引⽤async-validator库来做校验,这⾥为了简便直接做⾮空校验 validateField = field => {
// this.options数据格式如下↓↓↓
// {
// "username": {
// "rules": [{
// "required": true,
// "message": "请填写⽤户名"
// }]
// },
// "password": {
// "rules": [{
// "required": true,
// "message": "请填写密码"
// }]
// }
// }
const { rules } = this.options[field]
const ret = rules.some(rule => {
if (quired) {
if (!this.state[field]) {
this.setState({
[field + 'Message']: ssage
})
// this.state数据格式如下↓↓↓
// {"username":"","usernameMessage":"","password":"","passwordMessage":""} return true// 校验失败,返回true
}
}
})
if (!ret) {
// 校验成功,将错误信息清空
this.setState({
[field + 'Message']: ''
})
}
return !ret
};
// 校验所有字段
validate = cb => {
const rets = Object.keys(this.options).map(field =>
this.validateField(field)
)
// 如果校验结果数组中全部为true,则校验成功
const ret = rets.every(v => v === true)
cb(ret)
};
getFieldDecorator = (field, option) => InputComp => {
this.options[field] = option
return (
<div>
{/* 把已经存在的jsx克隆⼀份,并修改它的属性,直接修改属性是不允许的。
这⾥在更⾼级别定义onChange事件,控制元素的值,
这样当组件发⽣变化时,就不⽤进⾏组件之间的来回通信 */}
{React.cloneElement(InputComp, {
name: field, // 控件name
value: this.state[field] || '', // 控件值
onChange: this.handleChange // change事件处理
})}
</div>
)
};
render() {
return (
<Comp
{...this.props}
getFieldDecorator={FieldDecorator}
validate={this.validate}
/
>
)
}
}
}
@FormCreate
class MForm extends Component {
onSubmit = () => {
this.props.validate(isValid => {
if (isValid) {
alert('校验成功,可以登录了')
console.log(this.props.value)
} else {
alert('校验失败')
}
})
};
render() {
const { getFieldDecorator } = this.props
return (
<div>
⽤户名:{getFieldDecorator('username', {
rules: [{ required: true, message: '请填写⽤户名' }]
})(<input type='text' />)}
密码:{getFieldDecorator('password', {
rules: [{ required: true, message: '请填写密码' }]
})(<input type='password' />)}
<button onClick={Submit}>Log in</button>
</div>
)
}
}
export default MForm
step4 - 增加表单输⼊时实时校验并提⽰错误逻辑,封装FormItem组件来展⽰错误信息,封装Input组件,增加前缀图标。⾄此,整个MForm组件就编写完成了!
1 import React, { Component } from 'react'
2 import { Icon } from 'antd'
3
4// hoc: 包装⽤户表单,增加数据管理能⼒及校验功能
5 const FormCreate = Comp => {
6return class extends Component {
7 constructor(props) {
8 super(props)
9this.options = {} // 保存字段选项设置
10this.state = {} // 保存各字段的值
11 }
12
13// 处理表单项输⼊事件
14 handleChange = e => {
15 const { name, value } = e.target
16this.setState(
17 {
18 [name]: value
19 },
20 () => {
21// 处理状态变化后的校验
22// 由于setState是异步的,所以这⾥需要在回调函数中处理后续操作
23// 保证状态已经完成改变
24this.validateField(name)
25 }
26 )
27 };
28
29// 表单项校验,可以引⽤async-validator库来做校验,这⾥为了简便直接做⾮空校验 30 validateField = field => {
31// this.options ↓↓↓
32// {
33// "username": {
34// "rules": [{
35// "required": true,
36// "message": "请填写⽤户名"
37// }]
38// },
39// "password": {
40// "rules": [{
41// "required": true,
42// "message": "请填写密码"
43// }]
44// }
45// }
46 const { rules } = this.options[field]
47 const ret = rules.some(rule => {
48if (quired) {
49if (!this.state[field]) {
50this.setState({
51 [field + 'Message']: ssage
52 })
53// this.state ↓↓↓
54// {"username":"","usernameMessage":"","password":"","passwordMessage":""} 55return true// 校验失败,返回true
56 }
57 }
58 })
59if (!ret) {
60// 校验成功,将错误信息清空
61this.setState({
62 [field + 'Message']: ''
63 })
64 }
65return !ret
66 };
67
68// 校验所有字段
69 validate = cb => {
70 const rets = Object.keys(this.options).map(field =>
71this.validateField(field)
72 )
73// 如果校验结果数组中全部为true,则校验成功
74 const ret = rets.every(v => v === true)
75 cb(ret)
76 };
77
78 getFieldDecorator = (field, option) => InputComp => {
79this.options[field] = option
80return (
81 <div>
82 {/* 把已经存在的jsx克隆⼀份,并修改它的属性,直接修改属性是不允许的。
83这⾥在更⾼级别定义onChange事件,控制元素的值,
84这样当组件发⽣变化时,就不⽤进⾏组件之间的来回通信 */}
85 {React.cloneElement(InputComp, {
86 name: field, // 控件name
87 value: this.state[field] || '', // 控件值
88 onChange: this.handleChange, // change事件处理
89 onFocus: this.handleFocus
90 })}
91 </div>
92 )
93 };
94
95// 控件获取焦点事件
96 handleFocus = e => {
97 const field = e.target.name
98this.setState({
99 [field + 'Focus']: true
100 })
101 }
102
103// 判断控件是否被点击过
104 isFieldTouched = field => !!this.state[field + 'Focus']
105
106// 获取控件错误提⽰信息
107 getFieldError = field => this.state[field + 'Message']
108
109 render() {
110return (
111 <Comp
112 {...this.props}
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论