Nur eine andere Sache für diejenigen, die eine vollständig kontrollierte Formularkomponente erstellen möchten, ohne eine übergroße Bibliothek zu verwenden.
ReduxFormHelper - eine kleine ES6-Klasse mit weniger als 100 Zeilen:
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}
}
}
Es erledigt nicht die ganze Arbeit für Sie. Es erleichtert jedoch die Erstellung, Validierung und Handhabung einer kontrollierten Formularkomponente. Sie können den obigen Code einfach kopieren und in Ihr Projekt einfügen oder stattdessen die entsprechende Bibliothek hinzufügen -redux-form-helper
(Plug!).
Wie benutzt man
Der erste Schritt besteht darin, dem Redux-Status bestimmte Daten hinzuzufügen, die den Status unseres Formulars darstellen. Diese Daten enthalten aktuelle Feldwerte sowie eine Reihe von Fehlerflags für jedes Feld im Formular.
Der Formularstatus kann zu einem vorhandenen Reduzierer hinzugefügt oder in einem separaten Reduzierer definiert werden.
Darüber hinaus müssen Sie eine bestimmte Aktion definieren, die die Aktualisierung des Formularstatus initiiert, sowie den jeweiligen Aktionsersteller.
Aktionsbeispiel :
export const FORM_UPDATE = 'FORM_UPDATE'
export const doFormUpdate = (data, errors) => {
return { type: FORM_UPDATE, data, errors }
}
...
Reduzierbeispiel :
...
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 || {})
}
...
}
}
Der zweite und letzte Schritt besteht darin, eine Containerkomponente für unser Formular zu erstellen und diese mit dem jeweiligen Teil des Redux-Status und der Aktionen zu verbinden.
Außerdem müssen wir ein Formularmodell definieren, das die Validierung von Formularfeldern spezifiziert. Jetzt instanziieren wirReduxFormHelper
Objekt als Mitglied der Komponente und übergeben dort unser Formularmodell und eine Rückruf-Dispatching-Aktualisierung des Formularstatus.
Dann render()
müssen wir in der Methode der Komponente onChange
die onSubmit
Ereignisse jedes Felds und des Formulars mit bindenprocessField()
bzw. processForm()
Methoden verknüpfen und Fehlerblöcke für jedes Feld anzeigen, abhängig von den Formularfehlerflags im Status.
Im folgenden Beispiel wird CSS aus dem Twitter Bootstrap-Framework verwendet.
Beispiel für eine Containerkomponente :
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)
Demo
redux-forms
? Ich frage mich, wie diese Kesselplatte im Vergleich zu Reaktionsformen skaliert