概览
本文主要用于记录该次使用Formik时用到的相关接口,而侧重点不在antd-mobile,对antd-mobile会贴出对应组件API。
文章需要基础知识点:
- React基本知识
- ES6基本知识
文章实践环境为:
- antd-mobile@2.2.3
- formik@1.1.1
- react@16.4.1
文章最后成果:
- 熟悉Formik的使用方法
- 封装一个简单的自用表单组件
Formik
开源的轻量级 React 表单组件。
GitHub地址:Formik
该表单组件主要解决以下三个问题:
- 表单的输入输出
- 验证表单输入并提供错误信息
- 控制表单的提交
至于为什么不用 redux-form 作者也给出了解释:
- 表单的状态是短暂而局部的,所以在Redux(或任何类型的Flux库)中追踪它是不必要的。
- Redux-Form 在你每输入一个字母都会调用一次顶级的Reducer,当项目逐渐变大,这一点会导致输入的延迟变得明显。
- Redux-Form 经过gzip压缩后22.5 kB(而 Formik 仅有7.8 kB)
antd-mobile
ant-design的移动版UI组件库。
官网:antd-mobile
不需要多做介绍了,每个写React的人都应该听说或用过antd系列的UI组件库吧。
Formik 使用方法
-
withFormik()
HOC方法 -
<Formik />
React组件
两者使用内在其实是相同,在可控性上React组件方式会更好,对此可以不需要太在意,你可以随时在两种使用方法间做转换。
本文上篇主要讲述Formik的HOC方法下的基本封装,下篇则侧重于封装常用组件并对Formik两种使用方法的切换做一次简单的总结。
官方示例解读
// Higher Order Component
import React from 'react';
import { withFormik } from 'formik';
// Our inner form component which receives our form's state and updater methods as props
// 这就是个组件,传入的props是由withFormik这个HOC函数内部做处理。
const InnerForm = ({
values, // 表单中的值,为一个对象{}
errors, // 用于报错提示的信息,为一个对象{}
touched, // 用于检查用户是否点击过该表单项,为一个对象{}
handleChange,
/* handleChange:默认的值修改回调函数,传入参数为 e: React.ChangeEvent<any> 对象。
所以如果使用antd-mobile组件,由于某些组件传入参数为 value 值,因此需要对此进行一定程度上的封装*/
handleBlur,
/* handleBlur:失去焦点时默认的回调函数,传入参数为 event 对象。
需要自定义时也需要对应的封装。这个函数和handleChange都是DOM-only的函数 */
handleSubmit, // 传入参数为 e: React.FormEvent<HTMLFormEvent> 对象
isSubmitting, // isSubmitting表示表单提交的状态
}) => (
<form onSubmit={handleSubmit}>
<input
type="email"
name="email"
onChange={handleChange}
onBlur={handleBlur}
value={values.email}
/>
// 判断该项 被点击过、具有错误信息 的情况下,显示错误消息<div>{errors.email}</div>。
{touched.email && errors.email && <div>{errors.email}</div>}
<input
type="password"
name="password"
onChange={handleChange}
onBlur={handleBlur}
value={values.password}
/>
{touched.password && errors.password && <div>{errors.password}</div>}
<button type="submit" disabled={isSubmitting}>
Submit
</button>
</form>
);
// Wrap our form with the using withFormik HoC
// 具体参数可以看这里
const MyForm = withFormik({
// Transform outer props into form values
// 将外部传入props设为表单的值,如 email: props.email 就可设置表单的值
mapPropsToValues: props => ({ email: '', password: '' }),
// Add a custom validation function (this can be async too!)
// 在submit前会调用该函数对值进行检查,如果errors为{}则会执行handleSubmit
validate: (values, props) => {
const 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;
},
// Submission handler
// 传入参数为表单提交时的值与一个FormikBag对象,该对象具有传入的props和method,具体可以查看对应文档描述
handleSubmit: (
values,
{
props,
setSubmitting,
setErrors /* setValues, setStatus, and other goodies */,
}
) => {
// 具体submit后执行函数,LoginToMyApp不必过多关心。
// 示例就是进行了一个异步请求并对返回的结果做处理。
LoginToMyApp(values).then(
user => {
setSubmitting(false);
// do whatevs...
// props.updateUser(user)
},
errors => {
setSubmitting(false);
// Maybe even transform your API's errors into the same shape as Formik's!
setErrors(transformMyApiErrors(errors));
}
);
},
})(InnerForm);
// Use <MyForm /> anywhere
const Basic = () => (
<div>
<h1>My Form</h1>
<p>This can be anywhere in your application</p>
<MyForm />
</div>
);
export default Basic;
官方示例的使用方法比较清晰,我们在该基础上逐步修改即可。
掌握官方示例中涉及的使用方法
示例中,我们可以看出主要需要掌握的很少,const InnerForm
仅仅是一个无状态组件。const MyForm
是通过HOC方法返回的一个表单组件。
const InnerForm = ({
values,
errors,
touched,
handleBlur,
handleChange,
handleSubmit,
isSubmitting
}) => (<form></form>)
其中前三项values为表单值、errors为错误记录、touched为是否点击过对应表单记录。
而handleBlur和handleChange都是DOM-only的回调函数,接受传入参数为 Event 对象,如果我们在表单中使用第三方组件,则需要用到另外两个方法。
- setFieldValue(field: string, value: any, shouldValidate?: boolean)
- setFieldTouched(field: string, isTouched: boolean, shouldValidate?: boolean)
其中第三个参数是设置是否跳过验证。
再看
const MyForm = withFormik({
mapPropsToValues,
validate,
handleSubmit
})
通过HOC方法,实现了对InnerForm
表单设置默认值、表单值验证、表单提交三个方法。
- mapPropsToValues: props=>({...}),传入组件props,返回InnerForm中的values对象
- validate: (values,props)=>({...}),传入InnerForm提交的values对象与组件props,返回errors对象
- handleSubmit: (values,FormikBag)=>(void),传入values对象与FormikBag对象,不需返回值
FormikBag
对象属性:
props
resetForm
setErrors
setFieldError
setFieldTouched
setFieldValue
setStatus
setSubmitting
setTouched
setValues
值得注意的是,FormikBag
不包含所有事件处理函数与errors,status,touched
,FormikBag
提供的是在表单submit后,对表单的处理所需的函数,比如通过setFieldError函数,提供如 用户名重复
之类的错误信息。
初步与antd-mobile组件进行组合
从上一步可以得知,我们主要的渲染部分放在了InnerForm
组件中,所以这次我们把目光放在InnerForm组件中即可。
该步骤使用到的antd-mobile组件为:
首先写入InnerForm
组件中我们需要的参数和基本骨架:
const InnerForm = ({
values,
errors,
touched,
handleBlur,
setFieldTouched,
setFieldValue,
handleSubmit,
isSubmitting
}) => (<form onSubmit={handleSubmit}>
<WingBlank>
<List>
</List>
</WingBlank>
</form>)
接下来我们添加一个输入框用于接收用户输入的email地址。
在<List></List>
中插入InputItem
组件如下:
<List>
<InputItem
onChange={(value)=>setFieldValue('email',value)}
onBlur={()=>{setFieldTouched('email',true)}}
value={values.email}
touched={touched.email}
errors={errors.email}
/>
</List>
这里值得注意,首先我们看看InputItem组件的文档,onChange
和onBlur
的默认参数都是string
,而不是Event
对象,在这里我们需要弃用官方文档中的handleBlur
和handleChange
两个回调参数,手动的调用setFieldValue
和setFieldTouched
来完成相应的效果。
为组件设置错误提示
如果使用官方文档使用的
{touched.password && errors.password && <div>{errors.password}</div>}
进行错误提醒,那么重复的代码工作量会略大了,在这里我们把错误提示做成一个组件。
具体代码如下:
const MyErrorItem = props =>
props.touched && props.errors ? (
<List.Item style={{ backgroundColor: "#eee" }}>
<span style={{ color: "red", fontSize: ".7rem"}}>* {props.errors}</span>
</List.Item>
) : null;
那么错误提醒的代码就缩减成了
<MyErrorItem touched={touched.password} errors={errors.password} />
再次通过HOC我们可以将整个InputItem
组件与MyErrorItem
组件组合在一起。
function HOCErrorInItem(NormalComponent, ErrorComponent) {
return class HOCErrorFormItem extends React.Component {
render(){
return (<div>
<NormalComponent {...this.props} />
<ErrorComponent
touched={this.props.touched}
errors={this.props.errors}
/>
</div>)
}
}
}
const MyInputItem = props =>(
<InputItem
type={props.type}
name={props.name}
onChange={value => props.onChange(value)}
onBlur={props.onBlur}
value={props.value}
>
{props.label}
</InputItem>
)
const InputItemWithErrorTip = HOCErrorInItem(MyInputItem,MyErrorItem)
这样我们需要就封装好了一个附带错误提示的输入框组件。
到这一步,我们的InnerForm
组件应该如下:
const InnerForm = ({
values,
errors,
touched,
handleBlur,
setFieldTouched,
setFieldValue,
handleSubmit,
isSubmitting
}) => (<form onSubmit={handleSubmit}>
<WingBlank>
<List>
<InputItemWithErrorTip
type="email"
name="Email"
label="Email"
onChange={value => setFieldValue("email", value)}
onBlur={()=>{setFieldTouched('email',true)}}
value={value.email}
touched={touched.email}
errors={errors.email}
/>
</List>
</WingBlank>
</form>)
该文上篇到此结束,这次主要是工作时候作为记录而言描写以下自己的思路,毕竟好记性不如烂笔头,也算是简单梳理一下思路。
具体补充将在下篇完成,内容应该包括各类型表单组件封装,工作完成后对封装组件的反思,下篇完成时间尽量在9月中完成,目前代码仍然处于封装各个组件的阶段。
动笔时间也与上次相比相隔甚久了,感觉自己还是摸索路上,趁着工作压力不大,多看多读多想多写吧。
=_=每天动工一点点,优先满足工作需要。
欢迎大家的一切快乐讨论。
目前施工现场:
CodeSandBox
Github
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。