使用Formik輕鬆開發更高質量的React表單(三) 解析

提醒和建議



根據個人粗淺經驗,若是您對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模式。請參考下面的代碼:react

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共提供了三種渲染方法,它們是:redux

  • <Formik component>

  • <Formik render>

  • <Formik children>

Formik props列表分析



上面全部三種渲染方法都會傳遞相同的props。接下來,咱們對props每個份量做相應的解釋。app

(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屬性,那麼此函數會肯定錯誤對象的具體信息。ide

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標籤。ui

【注意】本屬性僅適用於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中的相匹配。

相關文章
相關標籤/搜索