如何实现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小时内删除。