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/

相关文章
相关标签/搜索