使用Formik轻松开发更高质量的React表单(三)<Formik />解析

提醒和建议



根据我的粗浅经验,如果您对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中的相匹配。

相关文章
相关标签/搜索