redux-form V.7.4.2學習筆記(六)表單同步校驗技術

一、客戶端同步校驗支持

redux-form V.7.4.2提供了兩種方法可以爲表單提供同步客戶端校驗支持。

第一種是爲整個redux-form提供校驗函數,該函數接受一個以表單中所有值組成的對象作爲參數並返回一個帶有所有錯誤信息的對象。具體實現方式也分爲兩種形式:(1)通過將校驗函數作爲配置參數提供給經過裝飾的表單組件來完成的;(2)作爲props提供給經過裝飾的表單組件來完成的。這也是本文要討論的校驗方案。

第二種是爲每個字段使用單獨的校驗器。後面文章中我們也會討論字段級表單驗證的示例。

此外,還可以爲redux-form提供具有與校驗函數相同類型簽名的警告函數。警告方式並不是將表單標記爲無效的,這在某種意義上允許兩種層次嚴重錯誤的發生(這個結論只有通過深入分析有關源碼才能徹底弄明白,由於時間原因我也沒有分析這部分源碼,所以在此先略去進一步討論)。

另外需要提示的是,這裏提供的示例校驗函數純粹是爲了簡化演示目的。在實際的應用程序中,建議構建某種類型的可重用校驗系統。

【注意】通常使用可重用的無狀態函數組件來渲染每個字段。重要的是,這不是使用內聯方式定義的(在render函數中),因爲在每次渲染時以及觸發每一個字段的重新渲染時都要進行校驗——因爲此時組件prop將發生變化。

幾個重要結論

(1)如果校驗函數返回錯誤,並且表單當前由於發生各種錯誤而沒有渲染字段,則表單將被視爲有效的並會被提交。

(2)表單數據每次更改時都會進行同步校驗;因此,如果你的字段值無效,則將始終存在一個field.error值。一旦你的字段被修改了,你可能只想顯示校驗錯誤——這種情況下當你的字段上發生onBlur事件時redux-form會爲設置一個專門的標記。最終,當提交表單時,所有字段都標記爲已觸摸(也就是已修改,一些特殊情況下,例如系統已經內置提供了某種數據,如果你點擊一下對應字段,說明「已觸摸」,這種情況也算是「已修改」的特殊情形),從而允許顯示任何校驗錯誤。

(3)在你的校驗函數中,值可能未定義,因此在驗證嵌套字段時請特別注意。如果沒有注意這個問題,那麼你可能會遇到一些“TypeError:undefined”這樣的錯誤提示。

本文將簡要討論redux-form支持下的同步表單校驗支持,包括了錯誤和警告型配置。官方Demo中只演示了輸入框的校驗,而這裏準備了包括 radio selecttextarea 的校驗方式。~~

官方示例分析

官方示例的核心文件是redux-form-synchronous-validation.js,其中文件與絕大部分的其他實例保持一致,在此不作贅述。

運行時快照

redux-form V.7.4.2學習筆記(六)表單同步校驗技術

注意到,上圖中在尚未點按提交按鈕的情況下,無論是輸入了不合格式要求的郵箱數據,還是輸入不在指定範圍內的年齡數據,校驗子系統都給出相應的錯誤提示信息。從源碼跟蹤分析來看,redux-form這個庫的核心是一個不到900行代碼的名爲createReduxForm.js的文件。上圖中錯誤信息都是由React.js系統完成的。也就是說,redux-form只提供校驗、錯誤、警告等數據部分(包括判斷函數)的抓取分析,最終的可能的錯誤提示是交由React系統(通過createElement方法)完成的。

代碼分析

在此,我們只關注與表單整體校驗有關的部分。下面還是先來看一下實例代碼(SyncValidationForm.js,僅直接相關部分):

const validate = values => {
  const errors = {}
  if (!values.username) {
    errors.username = 'Required'
  } else if (values.username.length > 15) {
    errors.username = 'Must be 15 characters or less'
  }
  if (!values.email) {
    errors.email = 'Required'
  } else if (!/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i.test(values.email)) {
    errors.email = 'Invalid email address'
  }
  if (!values.age) {
    errors.age = 'Required'
  } else if (isNaN(Number(values.age))) {
    errors.age = 'Must be a number'
  } else if (Number(values.age) < 18) {
    errors.age = 'Sorry, you must be at least 18 years old'
  }
  return errors
}

const warn = values => {
  const warnings = {}
  if (values.age < 19) {
    warnings.age = 'Hmm, you seem a bit young...'
  }
  return warnings
}

const SyncValidationForm = (props) => {
  //......
}

export default reduxForm({
  form: 'syncValidation',  // a unique identifier for this form
  validate,                // <--- validation function given to redux-form
  warn                     // <--- warning function given to redux-form
})(SyncValidationForm)

其實,上面代碼中主要有兩句話:
(1)reduxForm函數中出現了validate和warn兩個參數(ES6對象屬性的簡潔表示方式),這兩個鍵值定義如上;
(2)上面定義了兩個相應於(1)中命名的函數,分別實現規則範圍內數據的判定與可能的錯誤及警告信息的確定。

再補充一點的話,reduxForm函數中出現的validate和warn兩個參數,在官方API中已有明確功能說明,各自的英文說明如下(因爲比較規則,在此不作翻譯):
(1)關於validate函數
//validate : (values:Object, props:Object) => errors:Object [optional]
//a synchronous validation function that takes the form values and props passed
// into your component. If validation passes, it should return {}.
// If validation fails, it should return the validation errors in the form { field1: <String>, field2: <String> }.
// Defaults to (values, props) => ({}).

(2)關於warn函數
//warn : (values:Object, props:Object) => warnings:Object [optional]
//a synchronous warning function that takes the form values and props passed into your component.
// Warnings work the same as validations, but do not mark a form as invalid.
// If the warning check passes, it should return {}. If the check fails, it should return the
// warnings in the form { field1: <String>, field2: <String> }. Defaults to (values, props) => ({}).

可見,這兩個函數都返回一個簡單包含相應信息的對象。當然,在一切順利的情況下,其返回的對象應該是{}。至於這些信息的最終渲染是交由React系統實現的。

補充

參考資料(1)中tedyuen提供的基於官方示例的修改版本值得有興趣的朋友進一步試驗學習,畢竟官方示例只是提供了一種指導思想,實際的開發卻要求你必須去面對各種複雜的表單組件的校驗問題。

參考

(1)https://github.com/tedyuen/react-redux-form-v6-example#snycValidation
(2)https://redux-form.com/7.4.2/examples/syncvalidation/

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章