您好,登錄后才能下訂單哦!
原本我想把整個redux-form中知識點與使用技巧通過這個小系列短文全面總結出來的,但是此過程中發現問題的確不少。但同時,在參考國內外一些相關資源的同時,又發現了一個比redux-form更值得研究的東西。先賣個關子(可能有少數朋友已經有所了解),稍過一些時間我會專門撰文介紹。因此,臨時把本文作為此小系列的結束篇,敬請有志于學習redux-form有朋友原諒(當然,在你了解到我介紹的那個更好用的form wrapper后你更為有所體諒)。
redux-form官方網站提供了操作form的許多API,其中最重要的無外乎三個:reduxForm(config:Object) 、props和 <Field/>。
本文專注于分析<Field/>的基本使用方法及注意事項,但是閱讀本語言的前提是需要你先初步掌握reduxForm(config:Object) 和props這兩個API的使用。有關這兩個API,在前面的幾篇中我們已經探討了許多,其實還有很多更深入的內容會在接下來的文章中進行解析。使用一個單篇來介紹<Field/>這個API的另一個原因是,這個API本身比較復雜與典型,掌握了這個API使用后,你會輕松把握另外兩個相關聯的API:<Fields/>和<FieldArray/>。
<Field/>遠非官方基本示例中使用得那么簡單。其實,所有需要與Redux store 數據連接的表單中的輸入組件(包括可能的多層次嵌套輸入組件),都可以用 <Field/>實現。
在正確使用<Field/>這個API之前,你需要牢記下面三條基本原則:
此屬性值可以是簡單的字符串,如 userName、password,也可以是使用點和中括號串聯表達的代表路徑含義的復雜的字符串結構,如 contact.billing.address[2].phones[1].areaCode。
此屬性值可以是一個React組件(Component)、無狀態函數(stateless function)或者是DOM所支持的默認的標簽(分別是input、textarea和select)元素。
能夠傳遞給<Field/>的屬性有許多。如前方所強調的,其中name和component屬性是必須提供的,截止目前最新版本為止,<Field/>還有下面一些屬性是可選提供的:
官文原文中說法是“All other props will be passed along to the element generated by the component prop.”直譯過來,好像是“其他所有屬性會被傳遞給由component屬性生成的元素”。但是,這種理解本人感覺十分不妥,又有同學Ted Yuen的翻譯成“其他所有屬性會通過prop傳遞到元素生成器中。如 className。”感覺也不是很恰當。還是讓我們隨著本文最后面復雜的示例討論來確定如何理解這一句吧。
var Field = require('redux-form').Field; // ES5
import { Field } from 'redux-form'; // ES6
<Field/>組件的屬性中,component這個屬性相對比較復雜,也非常重要,下面作專門討論。從源碼分析來看,component屬性最終是被傳遞給React.createElement()的,于是component存在三種類型的取值,如下所示。
這種情形下 ,component屬性值可以是任何自定義的組件或者從其他第三方庫導入的React組件。定義組件的代碼請參考:
// MyCustomInput.js
import React, { Component } from 'react'
class MyCustomInput extends Component {
render() {
const { input: { value, onChange } } = this.props
return (
<div>
<span>The current value is {value}.</span>
<button type="button" onClick={() => onChange(value + 1)}>Inc</button>
<button type="button" onClick={() => onChange(value - 1)}>Dec</button>
</div>
)
}
}
然后在你的表單組件定義代碼中便可以這樣使用:
import MyCustomInput from './MyCustomInput'
...
<Field name="myField" component={MyCustomInput}/>
這是使用 <Field/> 的最靈活的方法,因為使用這種方法可以使你完全控制表單輸入組件的渲染方式。而且,這種方式對于顯示校驗錯誤信息特別有用。當然,這種使用思路對于從以前版本的 redux-form使用轉移過來的程序員來說是十分熟悉的。
【切記】必須在render() 方法外部定義上述無狀態函數;否則,它會隨著每次渲染都會被重新創建,從而由于組件的 prop發生了變化而使得系統強制 <Field/> 重新渲染。如果你在 render() 方法內部定義無狀態函數,這不但會拖慢你的app,而且input組件每次都會在組件重新渲染的時候失去焦點。
// outside your render() method
const renderField = (field) => (
<div className="input-row">
<input {...field.input} type="text"/>
{field.meta.touched && field.meta.error &&
<span className="error">{field.meta.error}</span>}
</div>
)
// inside your render() method
<Field name="myField" component={renderField}/>
這種使用情況比較簡單,比如創建一個文字輸入框組件,你只需要使用如下方式:
<Field component="input" type="text"/>
易見,這種方式是把傳統DOM元素用<Field/>API稍微一封裝,并指明相應的type屬性類型即可。
說明:本例中的代碼大部分來自于官方網站提供的redux-form-field-level-validation.js這個文件,也就是有關于在一個redux-form表單中進行按字段校驗的情況。但是,有幾句代碼作了修改,有興趣的同學請認真觀察分析(為了便于參考,我把這個文件的修改版本的完整代碼列舉如下):
import React from 'react'
import { Field, reduxForm } from 'redux-form'
const required = value => value ? undefined : 'Required'
const maxLength = max => value =>
value && value.length > max ? `Must be ${max} characters or less` : undefined
const maxLength25 = maxLength(15)
const number = value => value && isNaN(Number(value)) ? 'Must be a number' : undefined
const minValue = min => value =>
value && value < min ? `Must be at least ${min}` : undefined
const minValue18 = minValue(18)
const email = value =>
value && !/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i.test(value) ?
'Invalid email address' : undefined
const tooOld = value =>
value && value > 65 ? 'You might be too old for this' : undefined
const aol = value =>
value && /.+@aol\.com/.test(value) ?
'Really? You still use AOL for your email?' : undefined
const renderField = ({ input, label, type, custom001,meta: { touched, error, warning } }) => (
<div>
<div>
<span>input</span>
<pre>{JSON.stringify(custom001)}</pre>
</div>
<label>{label}</label>
<div>
<input {...input} placeholder={label} type={type}/>
{touched && ((error && <span>{error}</span>) || (warning && <span>{warning}</span>))}
</div>
</div>
)
const selects = [
{
text:'-- Select --',
value:'-1'
},
{
text:'Red',
value:'ff0000'
},
{
text:'Green',
value:'00ff00'
},
{
text:'Blue',
value:'0000ff'
}
]
const selectField = ({
input,
label,
selects,
meta: { touched, error, warning }
}) => (
<div>
<div>
<span>{label}</span>
<select {...input}>
{
selects.map((item, i) => (
<option key={i} value={item.value}>{item.text}</option>
))
}
</select>
</div>
{touched &&
((error && <div>{error}</div>) ||
(warning && <div>{warning}</div>))}
</div>
)
const FieldLevelValidationForm = (props) => {
const { handleSubmit, pristine, reset, submitting } = props
return (
<form onSubmit={handleSubmit}>
<Field name="username" type="text" custom001="custom00001" custom002="custom00002"
component={renderField} label="Username"
validate={[ required, maxLength25 ]}
/>
<Field name="email" type="email" custom03="custom00003" custom04="custom00004"
component={renderField} label="Email"
validate={email}
warn={aol}
/>
<Field name="age" type="number"
component={renderField} label="Age"
validate={[ required, number, minValue18 ]}
warn={tooOld}
/>
<Field validate={[required]} component={selectField} selects={selects} label="Favorite Color" name="favoriteColor"/>
<div>
<button type="submit" disabled={submitting}>Submit</button>
<button type="button" disabled={pristine || submitting} onClick={reset}>Clear Values</button>
</div>
</form>
)
}
export default reduxForm({
form: 'fieldLevelValidation' // a unique identifier for this form
})(FieldLevelValidationForm)
上述代碼中請注意如下幾點:
(1)<Field/>組件中仍然需要注意component屬性的用法,還有自定義屬性selects的用法;
(2)因為上面代碼的核心是討論逐字段校驗技術的,所以,仍然需要關注<Field/>組件中validate屬性的表達方式;
(3)再需要關注的是<Field/>組件中custom001等幾個屬性的用法(這里的表達毫無意義,只是概念探討)。然后,在上面的renderField這個無狀態函數(component屬性的用法之一,對不對?)相關代碼中,其參數使用了如下方式:
{ input, label, type, custom001,meta: { touched, error, warning } }
有關input和meta,官方網站已經提供了細致的說明,label和type對應于原始DOM標記的有關屬性,而custom001則是上面代碼中定義<Field/>組件時加入的一個定制屬性(prop),這里通過下面的簡單的測試方式提供了這個定制prop在<Field/>組件component屬性值(是一個無狀態函數)中的使用方法。
在redux-form中,<Field/>的位置舉足輕重,在本文相關的字段級校驗用法中,難點在于對其component不同屬性值(根據上面說明,可能是組件,無狀態函數或者常規DOM字符串)的屬性的理解,還有對于validate這個屬性值表達方式的理解。總體感覺,一下徹底掌握redux-form也不是一件容易事。總之,如果選擇了React,那么你會在這個“泥沼”中“滯留”很長一段時間。
1.https://redux-form.com/7.4.2/docs/api/field.md/
2.https://github.com/tedyuen/react-redux-form-v6-example#Field
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。