Chỉ một điều nữa cho những người muốn tạo thành phần biểu mẫu được kiểm soát hoàn toàn mà không sử dụng thư viện quá khổ.
ReduxFormHelper - một lớp ES6 nhỏ, dưới 100 dòng:
class ReduxFormHelper {
constructor(props = {}) {
let {formModel, onUpdateForm} = props
this.props = typeof formModel === 'object' &&
typeof onUpdateForm === 'function' && {formModel, onUpdateForm}
}
resetForm (defaults = {}) {
if (!this.props) return false
let {formModel, onUpdateForm} = this.props
let data = {}, errors = {_flag: false}
for (let name in formModel) {
data[name] = name in defaults? defaults[name] :
('default' in formModel[name]? formModel[name].default : '')
errors[name] = false
}
onUpdateForm(data, errors)
}
processField (event) {
if (!this.props || !event.target) return false
let {formModel, onUpdateForm} = this.props
let {name, value, error, within} = this._processField(event.target, formModel)
let data = {}, errors = {_flag: false}
if (name) {
value !== false && within && (data[name] = value)
errors[name] = error
}
onUpdateForm(data, errors)
return !error && data
}
processForm (event) {
if (!this.props || !event.target) return false
let form = event.target
if (!form || !form.elements) return false
let fields = form.elements
let {formModel, onUpdateForm} = this.props
let data = {}, errors = {}, ret = {}, flag = false
for (let n = fields.length, i = 0; i < n; i++) {
let {name, value, error, within} = this._processField(fields[i], formModel)
if (name) {
value !== false && within && (data[name] = value)
value !== false && !error && (ret[name] = value)
errors[name] = error
error && (flag = true)
}
}
errors._flag = flag
onUpdateForm(data, errors)
return !flag && ret
}
_processField (field, formModel) {
if (!field || !field.name || !('value' in field))
return {name: false, value: false, error: false, within: false}
let name = field.name
let value = field.value
if (!formModel || !formModel[name])
return {name, value, error: false, within: false}
let model = formModel[name]
if (model.required && value === '')
return {name, value, error: 'missing', within: true}
if (model.validate && value !== '') {
let fn = model.validate
if (typeof fn === 'function' && !fn(value))
return {name, value, error: 'invalid', within: true}
}
if (model.numeric && isNaN(value = Number(value)))
return {name, value: 0, error: 'invalid', within: true}
return {name, value, error: false, within: true}
}
}
Nó không làm tất cả công việc cho bạn. Tuy nhiên, nó tạo điều kiện cho việc tạo, xác nhận và xử lý một thành phần biểu mẫu được kiểm soát. Bạn chỉ có thể sao chép và dán mã trên vào dự án của mình hoặc thay vào đó, bao gồm thư viện tương ứng - redux-form-helper
(plug!).
Cách sử dụng
Bước đầu tiên là thêm dữ liệu cụ thể vào trạng thái Redux sẽ đại diện cho trạng thái của biểu mẫu của chúng tôi. Những dữ liệu này sẽ bao gồm các giá trị trường hiện tại cũng như tập hợp các cờ lỗi cho từng trường trong biểu mẫu.
Trạng thái biểu mẫu có thể được thêm vào một bộ giảm tốc hiện có hoặc được xác định trong một bộ giảm tốc riêng biệt.
Hơn nữa, cần xác định cập nhật hành động cụ thể bắt đầu trạng thái biểu mẫu cũng như người tạo hành động tương ứng.
Ví dụ hành động :
export const FORM_UPDATE = 'FORM_UPDATE'
export const doFormUpdate = (data, errors) => {
return { type: FORM_UPDATE, data, errors }
}
...
Ví dụ giảm tốc :
...
const initialState = {
formData: {
field1: '',
...
},
formErrors: {
},
...
}
export default function reducer (state = initialState, action) {
switch (action.type) {
case FORM_UPDATE:
return {
...ret,
formData: Object.assign({}, formData, action.data || {}),
formErrors: Object.assign({}, formErrors, action.errors || {})
}
...
}
}
Bước thứ hai và cuối cùng là tạo một thành phần chứa cho biểu mẫu của chúng tôi và kết nối nó với một phần tương ứng của trạng thái và hành động Redux.
Ngoài ra, chúng ta cần xác định một mô hình biểu mẫu xác định xác thực các trường biểu mẫu. Bây giờ chúng ta khởi tạo ReduxFormHelper
đối tượng với tư cách là thành viên của thành phần và chuyển qua đó mô hình biểu mẫu của chúng ta và một bản cập nhật gửi lại cuộc gọi của trạng thái biểu mẫu.
Sau đó, trong render()
phương thức của thành phần, chúng ta phải liên kết các sự kiện onChange
của từng trường và biểu mẫu onSubmit
với processField()
và processForm()
các phương thức tương ứng cũng như hiển thị các khối lỗi cho từng trường tùy thuộc vào các cờ lỗi của biểu mẫu trong trạng thái.
Ví dụ dưới đây sử dụng CSS từ khung Bootstrap của Twitter.
Ví dụ về Thành phần Container :
import React, {Component} from 'react';
import {connect} from 'react-redux'
import ReduxFormHelper from 'redux-form-helper'
class MyForm extends Component {
constructor(props) {
super(props);
this.helper = new ReduxFormHelper(props)
this.helper.resetForm();
}
onChange(e) {
this.helper.processField(e)
}
onSubmit(e) {
e.preventDefault()
let {onSubmitForm} = this.props
let ret = this.helper.processForm(e)
ret && onSubmitForm(ret)
}
render() {
let {formData, formErrors} = this.props
return (
<div>
{!!formErrors._flag &&
<div className="alert" role="alert">
Form has one or more errors.
</div>
}
<form onSubmit={this.onSubmit.bind(this)} >
<div className={'form-group' + (formErrors['field1']? ' has-error': '')}>
<label>Field 1 *</label>
<input type="text" name="field1" value={formData.field1} onChange={this.onChange.bind(this)} className="form-control" />
{!!formErrors['field1'] &&
<span className="help-block">
{formErrors['field1'] === 'invalid'? 'Must be a string of 2-50 characters' : 'Required field'}
</span>
}
</div>
...
<button type="submit" className="btn btn-default">Submit</button>
</form>
</div>
)
}
}
const formModel = {
field1: {
required: true,
validate: (value) => value.length >= 2 && value.length <= 50
},
...
}
function mapStateToProps (state) {
return {
formData: state.formData, formErrors: state.formErrors,
formModel
}
}
function mapDispatchToProps (dispatch) {
return {
onUpdateForm: (data, errors) => {
dispatch(doFormUpdate(data, errors))
},
onSubmitForm: (data) => {
// dispatch some action which somehow updates state with form data
}
}
}
export default connect(mapStateToProps, mapDispatchToProps)(MyForm)
Bản giới thiệu
redux-forms
? Tôi đang tự hỏi làm thế nào cái vảy nồi hơi đó so với dạng phản ứng