1. 程式人生 > >使用Formik輕松開發更高質量的React表單(三)<Formik />解析

使用Formik輕松開發更高質量的React表單(三)<Formik />解析

strong 禁止 修改 new equal 控制 形式 結合 dom

提醒和建議

根據我的粗淺經驗,如果您對Formik感興趣,並且想深入學習與使用這個庫,我建議您還是先對redux-form的使用邏輯與有關概念有所了解,而且理解和使用方面也變得容易得多的多。因為Formik中許多概念與形式與redux-form極其類似,但是各方面都簡化了很多,因為它不再依賴於約束整個前面存儲的Redux store的限制,由於整個前端使用一個store存儲,所以,隨著表單數量與形式變得越來越復雜,系統的屬性可能會受到嚴重影響——這是Formik產生的主要原因,Formik幹脆不使用Redux,而直接操作React組件,這就有可能使得編寫表單元素雖然簡單(HTML5)但比較冗長,於是Formik也引入了類似於redux-form的一些API與props等概念(但絕對有區別)。

另外請註意,為了與其他流行的React文章保持一致,有些單詞沒有必要翻譯過來,例如store, values,props,shape,errors,等等。另外,touchtouched這個詞在Formik中廣泛使用,意思是表單中某個字段被點擊過,此時用戶可能沒有輸入什麽數據,也有可能輸入了新的數據,都稱為touched。因此,後面的譯文中一般翻譯為「動過」,個別地方翻譯成「潤色」,請理解其使用情形就是。

關於<Formik />組件



<Formik>是一個幫助構建表單的組件,它也使用了類似於當前一些流行的庫(如React Motion和 React Router)的render這種prop模式。請參考下面的代碼:

import React from ‘react‘;
import { Formik } from ‘formik‘;

const BasicExample = () => (
  <div>
    <h1>My Form</h1>
    <Formik
      initialValues={{ name: ‘jared‘ }}
      onSubmit={(values, actions) => {
        setTimeout(() => {
          alert(JSON.stringify(values, null, 2));
          actions.setSubmitting(false);
        }, 1000);
      }}
      render={props => (
        <form onSubmit={props.handleSubmit}>
          <input
            type="text"
            onChange={props.handleChange}
            onBlur={props.handleBlur}
            value={props.values.name}
            name="name"
          />
          {props.errors.name && <div id="feedback">{props.errors.name}</div>}
          <button type="submit">Submit</button>
        </form>
      )}
    />
  </div>
);

Formik渲染方法



<Formik />這個API共提供了三種渲染方法,它們是:

  • <Formik component>

  • <Formik render>

  • <Formik children>

Formik props列表分析



上面所有三種渲染方法都會傳遞相同的props。接下來,我們對props每一個分量作相應的解釋。

(1)dirty: boolean

當values與初始值不絕對相等時這個屬性的值會返回true;否則返回false(Returns true if values are not deeply equal from initial values, false otherwise)。註意:dirty屬性是只讀的,不應該直接修改它。

(2)errors: { [field: string]: string }

其中包含Form校驗錯誤信息。這些信息應當與表單的定義於initialValues中的值(values)保持一致。如果你在使用validationSchema(也推薦你使用),那麽數據(原文使用的是“keys and shape”,關於shape一詞在React文章中經常見到,這裏不便直譯)應當與你的模式定義準確匹配。 從內部實現代碼來看,Formik會根據你提供的數據轉換原始的Yup校驗錯誤信息。如果你在使用validate屬性,那麽此函數會確定錯誤對象的具體信息。

handleBlur: (e: any) => void

這是onBlur對應的事件處理器函數。當你需要跟蹤某個輸入字段是否被“動過”(touched)時很有用。用法比如:<input onBlur={handleBlur} ... />

【註意】本屬性僅適用於DOM開發;如果是React Native開發則需使用setFieldTouched代替。

handleChange: (e: React.ChangeEvent<any>) => void

這是一個典型的輸入字段內容變化時要觸發的事件處理器。當key為事件發出的輸入字段的name屬性時這一調用會更新values。如果name不存在,那麽handleChange函數會進一步查找輸入字段(input)的id屬性。請註意: 這裏的input意指所有HTML input標簽。

【註意】本屬性僅適用於DOM開發;如果是React Native開發則需使用setFieldValue代替。

handleReset: () => void

這是表單復位處理器函數,調用它將把表單還原到初始值狀態。用法比如:<button onClick={handleReset}>...</button>

handleSubmit: (e: React.FormEvent<HTMLFormEvent>) => void

這是表單提交處理器函數。 用法比如:<form onSubmit={props.handleSubmit}>...</form>。請結合本系列文章第一篇中「表單提交原理」部分加以理解。

isSubmitting: boolean

這個屬性值代表了表單提交的當前狀態。如果表單在提交中將返回true;否則返回false。重要提醒:一旦你嘗試提交表單,Formik就會把這個值設置為true。請結合本系列文章第一篇中「表單提交原理」部分加以理解。

isValid: boolean

在不發生錯誤的情況下這個屬性值將為true;或者返回當表單處於pristine條件(例如沒有dirty)時會返回isInitialValid的結果值。

isValidating: boolean

如果Formik正在運行任何檢驗函數,則此屬性返回true;否則,返回false。想更多地了解在表單提交過程中isValidating屬性發生了什麽變化,請結合閱讀本系列文章第一篇中「表單提交原理」部分。

resetForm: (nextValues?: Values) => void

強行復位表單。這個調用會清除所有錯誤及字段「潤色」信息,並且設置isSubmitting為false,設置isValidating為false,並且把mapPropsToValues返回值設置為當前的WrappedComponent的props或者是傳遞過去的參數。註意:當在componentWillReceiveProps內部調用resetForm時,這是很有用的。

setErrors: (fields: { [field: string]: string }) => void

強行設置errors信息。

setFieldError: (field: string, errorMsg: string) => void

強行設置給定字段的error信息。註意,這裏的參數field應當匹配你希望更新的errors中的key。這個屬性在編寫定制的輸入錯誤信息處理器函數時非常有用。

setFieldTouched: (field: string, isTouched: boolean, shouldValidate?: boolean) => void

強行設置給定字段的touched狀態值。註意,這裏的參數field應當匹配你想更新的「動過」的key。這個屬性在編寫定制的blur處理器函數時非常有用。如果validateOnBlur的值設置為true(默認即為此值),那麽調用這個方法會觸發校驗運行。你也可以通過傳遞第三個參數為false來顯式地禁止或者跳過校驗。

submitForm: () => void

觸發表單提交操作。

submitCount: number

代表用戶嘗試提交表單的次數。當調用handleSubmit時此屬性的值會加1;但是調用handleReset後該屬性值會復位。請註意, submitCount是一個只讀屬性,不能直接修改。

setFieldValue: (field: string, value: any, shouldValidate?: boolean) => void

強制設置一個字段的值。其中,參數field應當匹配你希望更新的values中的key。 該屬性在編寫定制的change事件處理函數時很有用。當validateOnChange為true (默認即是這個值)時,調用此屬性對應的函數會觸發校驗的執行。當然,你還可以通過傳遞第三個參數為false來顯式地禁止或者跳過校驗。

setStatus: (status?: any) => void

強制設置一個頂級的狀態值。這個調用用於控制你的表單的任意頂級狀態。例如,你可以使用它來把API響應傳遞回在handleSubmit調用中的你的組件內部。

setSubmitting: (isSubmitting: boolean) => void

強制設置isSubmitting屬性的值。

setTouched: (fields: { [field: string]: boolean }) => void

強制設置touched屬性的值。

setValues: (fields: { [field: string]: any }) => void

強制設置values對象的值。

status?: any

這是一個頂級的狀態對象,你可以使用它來描述使用其他方法無法表達/存儲的表單狀態。在捕獲或者傳遞API響應給你的內部組件時這個屬性很有用。
【註意】你僅能通過調用setStatus: (status?: any) => void來修改status。

touched: { [field: string]: boolean }

此屬性用於潤色表單中對應的字段值。每一個鍵都相應於一個剛剛被「動過」(touched)或者訪問過的字段。

values: { [field: string]: any }

這是你的表單中的values對象。其中存儲了mapPropsToValues (如果指定的話)對應的結果數據,或者存儲那些傳遞給你的被包裝組件(wrapped component)的不是函數形式的props。
【說明】包裝組件(wrapped component)一詞在諸多React/Redux文章中多見,即是指使用類似於redux-form的reduxForm API封裝後的新組件。

validateForm: (values?: any) => void

根據指定的方式強制你的validate屬性對應的校驗函數或者是調用validateSchema。註意,你可以選擇性地傳遞數值進行校驗,當然這將相應地修改Formik狀態;否則,它會使用表單的當前values。

validateField: (field: string) => void

強制調用表單的validate屬性對應的函數——如果指定了相應字段的話。Formik將使用當前字段值。

component

下面給出此屬性的典型使用方法:

<Formik component={ContactForm} />;

const ContactForm = ({
  handleSubmit,
  handleChange,
  handleBlur,
  values,
  errors,
}) => (
  <form onSubmit={handleSubmit}>
    <input
      type="text"
      onChange={handleChange}
      onBlur={handleBlur}
      value={values.name}
      name="name"
    />
    {errors.name && <div>{errors.name}</div>}
    <button type="submit">Submit</button>
  </form>
};

【註意】<Formik component> 的優先級會高於<Formik render>;因此,你不要在同一個<Formik>中同時使用二者。

render: (props: FormikProps<Values>) => ReactNode

這個屬性相當重要,請參考如下代碼了解其用法:

<Formik render={props => <ContactForm {...props} />} />

<Formik
  render={({ handleSubmit, handleChange, handleBlur, values, errors }) => (
    <form onSubmit={handleSubmit}>
      <input
        type="text"
        onChange={handleChange}
        onBlur={handleBlur}
        value={values.name}
        name="name"
      />
      {errors.name &&
        <div>
          {errors.name}
        </div>}
      <button type="submit">Submit</button>
    </form>
  )}
/>

children: func

使用方法見下面的代碼片斷:

&lt;Formik children={props =&gt; &lt;ContactForm {...props} /&gt;} /&gt;

//或者使用下面的方式...

<Formik>
  {({ handleSubmit, handleChange, handleBlur, values, errors }) => (
    <form onSubmit={handleSubmit}>
      <input
        type="text"
        onChange={handleChange}
        onBlur={handleBlur}
        value={values.name}
        name="name"
      />
      {errors.name &&
        <div>
          {errors.name}
        </div>}
      <button type="submit">Submit</button>
    </form>
  )}
</Formik>

enableReinitialize?: boolean

默認值為false。此屬性用於在initialValues變化時控制是否重置表單。

isInitialValid?: boolean

默認值為false。用於控制表單加載前的isValid屬性的初始值。你也可以傳遞一個函數。此屬性用於當你想在表單初始加載時啟用/禁用一次提交/復位按鈕操作時。

initialValues?: Values

相應於表單初始字段值。Formik將使用這些值來生成例如props.values這樣的方法組件。

即使你的表單默認情況下為空,你也必須使用初始值來初始化所有字段;否則,React會拋出異步,說你已經把一個輸入字段從未控制(uncontrolled)狀態改變成了可控制(controlled)狀態。
【註意】 initialValues並不適用於高階組件(higher-order component);在高階組件情況下,你需要使用mapPropsToValues代之。

onReset?: (values: Values, formikBag: FormikBag) => void

對應於你可選擇使用的表單復位處理器函數。其中的參數是你的表單values,還有一個"FormikBag"值。

onSubmit: (values: Values, formikBag: FormikBag) => void

這是表單提交處理器函數。其中的參數是你的表單values,還有一個"FormikBag"值——其中提供了一個包含被註入的屬性(props)和方法(例如所有以set開頭——如set<Thing>的方法,還有方法resetForm)的子集的對象,以及傳遞給包裝組件的任何props。

【註意】 errors, touched,status以及所有的事件處理器函數都沒有包含在FormikBag參數中。

validate?: (values: Values) => FormikErrors<Values> | Promise<any>

【註意】Formik作用特別推薦我們使用validationSchema和Yup實現表單校驗。無論如何,表單校驗應該使用一種相對獨立的,比較直觀的方式實現——這也是每一個所希望的。

另外,你可以使用同步或者異步函數來實現表單校驗。請參考下面的代碼:

(1)同步校驗方式(返回一個errors對象)

// Synchronous validation
const validate = (values, props) => {
  let errors = {};

  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‘;
  }

  //...

  return errors;
};

(2)異步校驗方式(返回errors對象中的一個代表錯誤的Promise)

// Async Validation
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));

const validate = (values, props) => {
  return sleep(2000).then(() => {
    let errors = {};
    if ([‘admin‘, ‘null‘, ‘god‘].includes(values.username)) {
      errors.username = ‘Nice try‘;
    }
    // ...
    if (Object.keys(errors).length) {
      throw errors;
    }
  });
};

validateOnBlur?: boolean

此屬性的默認值為true。你可以在表單blur事件觸發時使用這個屬性——更具體一些說,無論是調用handleBlur,setFieldTouched還是setTouched都可以。

validateOnChange?: boolean

此屬性的默認值為true。當表單觸發change事件或者相關事件時你可以使用這個屬性告訴Formik進行有關校驗。 更具體一些說,無論是調用handleChange,setFieldValue還是setValues都可以。

validationSchema?: Schema | (() => Schema)

這個屬性非常重要,用於定義Yup模式( schema)或者是一個返回Yup模式的函數,用於校驗任務。其中,Errors會通過key映射到內部組件的errors。這個屬性的keys應當與values中的相匹配。

使用Formik輕松開發更高質量的React表單(三)<Formik />解析