react redux-form 使用

react中的表单验证, 输入限制等功能并不完善, 好在有redux-form库来提供比较方便的复杂表单实现功能.

注册reducer

1
2
3
4
5
6
7
import { combineReducers } from 'redux-immutable';
import { reducer as formReducer } from 'redux-form/immutable';

let _reducer = {};
_reducer['form'] = formReducer;

const RootReducer = combineReducers(_reducer);

实现常用的带验证输入框

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
//TextField.js

import React from 'react';
import classNames from 'classnames';
import { NgCmd, } from './../../_component';

const TextField = ({
input,
label,
labelClassName,
inputDivClassName,
className,
type,
maxLength,
row,
meta: { touched, error, warning },
}) => (
<div className={classNames(className, 'has-feedback', {
'has-error': touched && error,
'has-warning': touched && !error && warning,
'has-success': touched && !error && !warning,
})}>
<label className={classNames('control-label', labelClassName)}>{label}</label>
<div className={classNames(inputDivClassName, {
'hint--bottom-right hint-error': touched && error,
'hint--bottom-right hint-warning': touched && !error && warning,
})} aria-label={error || warning}>
<NgCmd if={!row}>
<input className="form-control" type={type || 'text'} maxLength={maxLength} {...input} />
</NgCmd>
<NgCmd if={!!row}>
<textarea className="form-control" row={row} maxLength={maxLength} {...input} />
</NgCmd>
<span className={classNames('glyphicon form-control-feedback', {
'glyphicon-remove': touched && error,
'glyphicon-warning-sign': touched && !error && warning,
'glyphicon-ok': touched && !error && !warning,
})} />
</div>
</div>
)

export default TextField;

//DateTimePickerField.js

import React from 'react';
import classNames from 'classnames';
import { DateTimePicker, } from './../../_component';

const DateTimePickerField = ({
input,
label,
labelClassName,
inputDivClassName,
className,
meta: { touched, error, warning },
...rest,
}) => (
<div className={classNames(className, 'has-feedback date-time-picker', {
'has-error': touched && error,
'has-warning': touched && !error && warning,
'has-success': touched && !error && !warning,
})}>
<label className={classNames('control-label', labelClassName)}>{label}</label>
<div className={classNames(inputDivClassName, {
'hint--bottom-right hint-error': touched && error,
'hint--bottom-right hint-warning': touched && !error && warning,
})} aria-label={error || warning}>
<DateTimePicker {...input} {...rest} />
<span className={classNames('glyphicon form-control-feedback', {
'glyphicon-remove': touched && error,
'glyphicon-warning-sign': touched && !error && warning,
'glyphicon-ok': touched && !error && !warning,
})} />
</div>
</div>
)

export default DateTimePickerField;

定义Form组件内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
const ImmutableForm = reduxForm({
form: personToDoItemFormName,
})((props) => {
const { handleSubmit, pristine, reset, submitting, submit } = props;
return (
<form onSubmit={handleSubmit} className='form-horizontal'>
<div className='form-group'>
<Field
name='ItemName'
type='text'
component={TextField}
label='事项名称'
maxLength='50'
validate={validateHelper({ required: true, tips: '事项名称必填', validates: 'validName' })}
warn={validateHelper({ minLength: 5, tips: '建议名称不少于5个字符' })}
labelClassName='col-sm-2'
inputDivClassName='col-sm-10'
/>
</div>
<div className='form-group'>
<Field
name='ItemContent'
row='3'
type='text'
component={TextField}
label='事项内容'
maxLength='500'
validate={validateHelper({ required: true, tips: '事项内容必填', maxLength: 500 })}
warn={validateHelper({ minLength: 5, tips: '建议内容不少于5个字符' })}
labelClassName='col-sm-2'
inputDivClassName='col-sm-10'
/>
</div>
<div className='form-group'>
<Field
name='ItemRemark'
row='3'
type='text'
component={TextField}
label='备注'
maxLength='500'
validate={validateHelper({ tips: '备注不能超过500字符', maxLength: 500 })}
labelClassName='col-sm-2'
inputDivClassName='col-sm-10'
/>
</div>
<div className='form-group'>
<Field
name='CompleteTime'
component={DateTimePickerField}
label='完成时间'
dateTimeformat={props.dateTimeformat}
dateTimestartDate={new Date().Format('yyyy-MM-dd hh:mm')}
dateTimeminView='0'
validate={validateHelper({ tips: '必须为日期格式', regex: /^\d{4}-\d{2}-\d{2} \d{2}:\d{2}$/ })}
labelClassName='col-sm-2'
inputDivClassName='col-sm-10'
/>
</div>
</form>
);
});

使用Form组件和表单事件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
const mapDispatchToProps = (dispatch) => ({
fn: {
openNewWindow: (windowParam) => {
dispatch(PopupWindowListAction.openNewWindow(windowParam));
},
alertMsg: (windowParam) => {
dispatch(PopupWindowListAction.alertMsg(windowParam));
},
closeWindow: (windowId) => {
dispatch(PopupWindowListAction.closeWindow({ windowId }));
},
getInitItemInfo: () => {
dispatch(Action.getInitItemInfo());
},
getItemInfo: (id) => {
dispatch(Action.getItemInfo(id));
},
initialize: (values, keepDirty) => {
dispatch(initialize(formName, values, keepDirty));
},
submit: () => {
dispatch(submit(formName));
},
touch: () => {
dispatch(touch(formName));
},
saveOrUpdateItem: (info) => {
dispatch(Action.saveOrUpdateItem(info));
},
},
});
clickSave() {
this.props.fn.touch();
if (this.props.formState.get('syncErrors')) {
let err = '';
_.forIn(this.props.formState.get('syncErrors'), function (value, key) {
err += value + '<br />';
});
this.props.fn.alertMsg(`表单未填写完成:<br />${err}`);
} else {
this.props.fn.saveOrUpdateItem(this.props.formState.get('values').toJS());
}
}

//render
<span className="btn btn-success btn-text" onClick={this.clickSave}>保存</span>
<ImmutableForm />

validateHelper

validateHelper是自己实现的一个简单的验证生成器, 支持缓存验证器, 多种内置验证器, 多个验证器, 自定义错误提示, 自定义正则验证即提示等功能. 具体使用方法可以看上面的例子.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
import Immutable from 'immutable';
import _ from 'lodash';

let validateHelpers = Immutable.Map();

const defaultValidates = {
validCode: {
test: /^[a-zA-Z_][a-zA-Z0-9._-]{1,49}$/i,
tips: `格式应该为code`,
},
validName: {
test: (v) => v.length >= 2 && v.length <= 50,
tips: `格式应该为name`,
},
validMoney: {
test: /(^[1-9][0-9]{0,5}(\.[0-9]{1,4})?$)|(^(0){1}$)|(^[0-9]\.[0-9]([0-9]){0,3}$)/i,
tips: `格式应该为money`,
},
};

function validateHelper(options = {}) {
const { required, maxLength, minLength, regex, tips, validates } = options;
const key = Immutable.fromJS({
required,
maxLength,
minLength,
regex,
tips,
validates,
});
if (validateHelpers.has(key)) {
return validateHelpers.get(key);
} else {
const fun = function (value, allValues, props, name) {
const valueStr = _.toString(value);
if (required && _.isEmpty(valueStr)) {
let defaultTips = '该字段必填';
return tips ? (_.isString(tips) ? tips : tips['required'] || tips.default || defaultTips) : defaultTips;
}
if (minLength && valueStr.length < minLength) {
let defaultTips = `该字段最小长度为${minLength}`;
return tips ? (_.isString(tips) ? tips : tips['minLength'] || tips.default || defaultTips) : defaultTips;
}
if (maxLength && valueStr.length > maxLength) {
let defaultTips = `该字段最大长度为${maxLength}`;
return tips ? (_.isString(tips) ? tips : tips['maxLength'] || tips.default || defaultTips) : defaultTips;
}
if (regex && !regex.test(value)) {
let defaultTips = `该字段格式不对`;
return tips ? (_.isString(tips) ? tips : tips['regex'] || tips.default || defaultTips) : defaultTips;
}
let validatesArr = _.isString(validates) ? [validates] : validates || [];
for (let i = 0; i < validatesArr.length; i++) {
var valid = defaultValidates[validatesArr[i]];
if (valid) {
if (_.isFunction(valid.test) && !valid.test(valueStr)) {
return tips ? (_.isString(tips) ? tips : tips.default || valid.tips) : valid.tips;
} else if (_.isFunction(valid.test.test) && !valid.test.test(valueStr)) {
return tips ? (_.isString(tips) ? tips : tips.default || valid.tips) : valid.tips;
}
} else {
throw `${validatesArr[i]}未找到`;
}
}
};
validateHelpers = validateHelpers.set(key, fun);
return fun;
}
}

export default validateHelper;
作者

Mosby

发布于

2017-10-23

许可协议

评论