这里的 formik 版本为:"formik": "^1.4.2"
API:jaredpalmer.com/formik/docs…
formik 是用来构建表单 Formik旨在轻松管理具有复杂验证的表单, Formik支持同步和异步表单级和字段级验证。
特性:
- 获取处于或不处于窗体状态值
- 验证表单和错误消息
- 处理表单提交
- 跟踪表单状态
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);
}}
>
复制代码
简单模式需要的三个要素:
-
待验证字段,规定formik对待验证字段的校验范围 initialValues 初始化值,在验证表单中待验证的字段。
-
验证规则 validate 具体校验规则,比如这里校验email的规则,对于新增的字段需要添加该字段对应的校验内容。
-
表单提交函数 onSubmit 函数为表单提交时触发的函数
表单中使用formik
现在我们已经有了验证表单的三要素,那在表单中具体是如何使用的呢?
其实是通过 formik 自带组件:
, , 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>
复制代码
而这里 为表单的主体, 为验证的具体字段, 为校验时具体的错误提示。
表单验证细节
验证方法触发的时机主要有两种:
-
当待校验字段的值发生变化时触发校验方法 当值发生变化会调用这几个方法: handleChange 这个方法中可以做具体的逻辑处理
setFieldValue setValues 这两个方法都可以为字段设置值 -
当待校验字段失去焦点时触发具体校验方法 当触发 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,当表单数据提交到校验函数中时,会异步运行所有字段级验证,深度合并结果
-
如果有错误:中止提交,isValidating 标识将设置成 false,设置 errors 并显示错误结果,同时设置 isSubmitting 为false,此时上面的代码可以看出表单提交的按钮被设置成不可提交状态。
-
如果验证通过: 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函数无法执行?