表單驗證之 formik 簡單用法

這裏的 formik 版本爲:"formik": "^1.4.2"css

API:jaredpalmer.com/formik/docs…react

formik 是用來構建表單 Formik旨在輕鬆管理具備複雜驗證的表單, Formik支持同步和異步表單級和字段級驗證。git

特性:github

  1. 獲取處於或不處於窗體狀態值
  2. 驗證表單和錯誤消息
  3. 處理表單提交
  4. 跟蹤表單狀態

formik 簡單模式

<Formik
    initialValues={{ email: '', password: '' }}
    validate={values => {
        let errors = {};
        if (!values.email) {
            errors.email = 'Required';
        } else if (
            !/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i.test(values.email)
        ) {
            errors.email = 'Invalid email address';
        }
        return errors;
    }}
    onSubmit={(values, { setSubmitting }) => {
        setTimeout(() => {
            alert(JSON.stringify(values, null, 2));
            setSubmitting(false);
        }, 400);
    }}
>
複製代碼

簡單模式須要的三個要素:api

  1. 待驗證字段,規定formik對待驗證字段的校驗範圍 initialValues 初始化值,在驗證表單中待驗證的字段。數組

  2. 驗證規則 validate 具體校驗規則,好比這裏校驗email的規則,對於新增的字段須要添加該字段對應的校驗內容。bash

  3. 表單提交函數 onSubmit 函數爲表單提交時觸發的函數異步

表單中使用formik

如今咱們已經有了驗證表單的三要素,那在表單中具體是如何使用的呢?函數

實際上是經過 formik 自帶組件:ui

, , and 來和 react 上下文掛鉤 formik

好比這樣的使用:

<Form>
    <Field type="email" name="email" />
    <ErrorMessage name="email" component="div" />
    <Field type="password" name="password" />
    <ErrorMessage name="password" component="div" />
    <button type="submit" disabled={isSubmitting}>
        Submit
    </button>
</Form>
複製代碼

而這裏 爲表單的主體, 爲驗證的具體字段, 爲校驗時具體的錯誤提示。

表單驗證細節

驗證方法觸發的時機主要有兩種:

  1. 當待校驗字段的值發生變化時觸發校驗方法 當值發生變化會調用這幾個方法: handleChange 這個方法中能夠作具體的邏輯處理
    setFieldValue setValues 這兩個方法均可覺得字段設置值

  2. 當待校驗字段失去焦點時觸發具體校驗方法 當觸發 blur 事件會調用的方法: handleBlur 表單字段失去焦點的時候觸發函數的具體邏輯 setTouched setFieldTouched 移動端對應函數

提交時觸發方法:handleSubmit submitForm 可強制調用的驗證方法:validateForm validateField

{({ errors, touched, validateField, validateForm }) => (
 <Form>
     <button type="button" onClick={() => validateForm().then(() => console.log('blah')))}>
        Validate All
    </button>
    <button type="submit">Submit</button>
</Form>
)}
複製代碼

校驗字段初始化支持對象和數組

在 Formik 的 initialValues 支持對象

initialValues={{
  social: {
    facebook: '',
    twitter: '',
  },
}}
複製代碼

在字段 Field 上使用:

支持數組:

initialValues={{
  friends: ['jared', 'ian'],
}}
複製代碼

在字段 Field 上使用:

表單提交數據

formik表單提交時候會觸發:handleSubmit(e)或submitForm 這兩個方法。

表單提交的各個階段:

預提交階段: 在這個階段,會初始化一個標識,該標識用來表示表單字段是否出現錯誤,校驗出錯將會把這個標識設置成false

isSubmitting 標識在預提交階段設置成true,這也是初始化值。 好比下面的代碼中能夠應用這個值來作表單提交按鈕是否可以點擊的狀態控制。

<button type="submit" disabled={isSubmitting}>
    Submit
</button>
複製代碼

驗證階段: 在這個階段,isValidating 仍然爲 true,當表單數據提交到校驗函數中時,會異步運行全部字段級驗證,深度合併結果

  1. 若是有錯誤:停止提交,isValidating 標識將設置成 false,設置 errors 並顯示錯誤結果,同時設置 isSubmitting 爲false,此時上面的代碼能夠看出表單提交的按鈕被設置成不可提交狀態。

  2. 若是驗證經過: isValidating 將設置成 false,表單將繼續提交,接下來 onSubmit 和 handleSubmit 方法將會被調用。

withFormik

withFormik 採用高階組件方式來處理表單驗證:

withFormik({對象裏面定義具體的驗證})(Form)

const formikForm = {
  mapPropsToValues: () => ({ email: '', password: '' }),
  handleSubmit: (values, formikBag) => {
    const { props: { login } } = formikBag;
    login(values, formikBag);
  },
  validate: (values) => {
    const errors = {};

    if (!values.email) {
      errors.email = 'Required';
    } else if (!format.validateEmail(values.email)) {
      errors.email = 'Invalid email address';
    }
    if (!values.password) {
      errors.password = 'Required';
    } else if (!/^(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{8,20}$/i.test(values.password)) {
      errors.password = consts.lowSecurity;
    }

    return errors;
  },
  displayName: 'SigninForm',
};

withFormik(formikForm)
複製代碼

在這裏的表單中,有兩個字段須要進行驗證:email 和password,因此咱們須要知道要將哪些字段交給formik進行驗證管理: 在 mapPropsToValues 方法中須要註明待驗證的字段,同時待驗證字段初始化爲空。

而 validate 方法是對待校驗的字段進行具體的驗證,在這個方法中會對待驗證的字段提供具體的驗證規則, 該函數返回一個錯誤Msg對象,Msg對象中存放的是對應驗證字段的錯誤提示。

displayName: 'SigninForm':

給無狀態函數組件提供一個合適的名稱,這樣在react devtools 能更容易找到。若是指定,則顯示爲formik(displayname),若是沒有則顯示 formik(組件),類組件不須要這個選項。總之這是一個標識性的做用。

mapPropsToValues: 有的話,那麼formik會將結果轉化爲可更新的表單狀態,並將這些值做爲props 提供給新組件

ErrorMessage 組件

若是字段被訪問時出現錯誤消息,錯誤消息是一個字符串

下面兩個代碼的效果是等同的:

<ErrorMessage name='email_code'>
    {msg => <p className={classNames(css.help, css['is-danger'])}>{msg}</p>}
</ErrorMessage>
複製代碼
{errors.name && touched.name ? (
    <div>{errors.name}</div>
) : null}
複製代碼

表單提交處理函數

handleSubmit: (values: Values, formikBag: FormikBag) => void values 表單中各個字段的值 formikbag 其中包含注入屬性和方法的子集(全部名稱以set或者resetForm開頭的方法),以及傳遞給包裝組件的如何屬性

onSubmit={(verifyValues, formikBag) => (withdrawAction(verifyValues, {
    ...formikBag,
    setPreStatus: setStatus,
    resetPreForm: resetForm,
}))}
複製代碼

這裏的是指在提交的時候,將這些參數經過調用父類傳遞過來的方法,將值傳遞過去 最終在action裏面對這些值進行處理

mapPropsToValues: ({ withdrawInfo }) => ({
    rid: '',
    amount: '',
    currency_type: 'coin',
    currency: withdrawInfo.get('currency'),
    receiveAmount: '0',
})
複製代碼

表單驗證初始化值,裏面的值和input 輸入框中的name或者value值對應。 可是這些初始化的值並不必定都是一一對應的,好比這裏的currency_type: 'coin', 對應的組件中就沒有這個input,那麼就有多是接口須要值 這些初始值在之後對input中的值進行修改後會發生變化

實現輸入6位驗證碼後,無需點擊確認按鈕,直接登陸

原代碼實現: 1.代碼邏輯:登陸框輸入內容驗證後,彈出驗證碼輸入框,輸入正確內容後,點擊確認便可登陸成功

表單嵌套: 代碼邏輯結構爲雙層表單,登陸框表單嵌套了驗證碼錶單。

登陸表單使用的是 withFormik -- formikForm 的形式來進行驗證

而驗證碼錶單初始化的驗證方式是 Formik。

而這裏的需求是須要輸入驗證碼後,達到驗證條件後直接調用登陸函數來驗證。

onSubmit={(emailValues, formikBag) => {
  login(emailValues, {
    ...formikBag,
    setStatus,
  });
}}
render={emailEle}
複製代碼

因而這裏的需求就須要對input輸入框的事件 onChange 進行處理,代替原來的 prop.handleChange

爲了保持原來驗證表單中的驗證的內容和具體輸入的數據,使得原有的驗證函數起效果,

因而驗證函數中的內容變成了這樣:

const {
  handleChange,
  submitForm,
} = props;

const emailCode = String(e.target.value).trim();
const code = format.digital('integer', 6)(emailCode);
e.target.value = code;

// 調用原來prop中的handleChange 函數
handleChange(e);
複製代碼

如今到了這裏,咱們的input輸入框中的value就能夠拿到輸入的內容:

value={props.values.email_code}
複製代碼

而且這些輸入的內容能夠經過原來的驗證表單的驗證函數繼續驗證,也就是說此時咱們只須要出發驗證碼錶單的提交事件就能夠將表單提交,可是這裏就出現了一個大的問題:

嘗試使用 Formik 中的 submitForm 方法發現不起效果

此時使用的是內層表單,也就是驗證碼錶單的提交表單的方法,可是發現,這個函數並不沒有發起請求,貌似沒有調用。而若是換成外層的登陸表單要進行提交的話,發現能夠發起請求,可是這個請求並無帶上驗證碼錶單的數據,因而這個方法無效。

因而在網上查找,發現 Formik 官方 github 中的一個 issue 提到了這個問題:

https://github.com/jaredpalmer/formik/issues/1218
複製代碼

上面提到了一個解決方案 setTimeout:

setTimeout(submitForm, 0);
複製代碼

使用 setTimeout 將表單提交函數包裹,這樣驗證表單的提交函數就能觸發提交表單

這裏將submitForm放入到下一個循環中去執行,這是由於當前的隊列中submitForm函數沒法執行?

相關文章
相關標籤/搜索