Formik与antd-mobile的表单实践(上)

2023-05-16

概览

本文主要用于记录该次使用Formik时用到的相关接口,而侧重点不在antd-mobile,对antd-mobile会贴出对应组件API。

文章需要基础知识点:

  1. React基本知识
  2. ES6基本知识

文章实践环境为:

  • antd-mobile@2.2.3
  • formik@1.1.1
  • react@16.4.1

文章最后成果:

  • 熟悉Formik的使用方法
  • 封装一个简单的自用表单组件

Formik

开源的轻量级 React 表单组件。 GitHub地址:Formik 该表单组件主要解决以下三个问题:

  1. 表单的输入输出
  2. 验证表单输入并提供错误信息
  3. 控制表单的提交

至于为什么不用 redux-form 作者也给出了解释:

  1. 表单的状态是短暂而局部的,所以在Redux(或任何类型的Flux库)中追踪它是不必要的。
  2. Redux-Form 在你每输入一个字母都会调用一次顶级的Reducer,当项目逐渐变大,这一点会导致输入的延迟变得明显。
  3. Redux-Form 经过gzip压缩后22.5 kB(而 Formik 仅有7.8 kB)

antd-mobile

ant-design的移动版UI组件库。 官网:antd-mobile 不需要多做介绍了,每个写React的人都应该听说或用过antd系列的UI组件库吧。

Formik 使用方法

  1. withFormik() HOC方法
  2. <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 对象,如果我们在表单中使用第三方组件,则需要用到另外两个方法。

  1. setFieldValue(field: string, value: any, shouldValidate?: boolean)
  2. setFieldTouched(field: string, isTouched: boolean, shouldValidate?: boolean) 其中第三个参数是设置是否跳过验证。

再看

const MyForm = withFormik({
    mapPropsToValues,
    validate,
    handleSubmit
})
复制代码

通过HOC方法,实现了对InnerForm表单设置默认值、表单值验证、表单提交三个方法。

  1. mapPropsToValues: props=>({...}),传入组件props,返回InnerForm中的values对象
  2. validate: (values,props)=>({...}),传入InnerForm提交的values对象与组件props,返回errors对象
  3. handleSubmit: (values,FormikBag)=>(void),传入values对象与FormikBag对象,不需返回值

FormikBag对象属性:

props resetForm setErrors setFieldError setFieldTouched setFieldValue setStatus setSubmitting setTouched setValues

值得注意的是,FormikBag 不包含所有事件处理函数与errors,status,touchedFormikBag提供的是在表单submit后,对表单的处理所需的函数,比如通过setFieldError函数,提供如 用户名重复 之类的错误信息。

初步与antd-mobile组件进行组合

从上一步可以得知,我们主要的渲染部分放在了InnerForm组件中,所以这次我们把目光放在InnerForm组件中即可。

该步骤使用到的antd-mobile组件为:

  • WingBlank
  • List
  • InputItem

首先写入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组件的文档,onChangeonBlur的默认参数都是string,而不是Event对象,在这里我们需要弃用官方文档中的handleBlurhandleChange两个回调参数,手动的调用setFieldValuesetFieldTouched来完成相应的效果。

为组件设置错误提示

如果使用官方文档使用的

{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

转载于:https://juejin.im/post/5b84f1c56fb9a019e73f7fe5

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Formik与antd-mobile的表单实践(上) 的相关文章

  • 使输入类型=“密码”在移动设备上使用数字键盘

    在我为移动设备设计的网站上 我有一个用于 PIN 码的输入字段 我希望在输入文本时隐藏文本 并且希望当移动设备上的用户想要输入 PIN 码时弹出数字键盘 当类型 数字 时 数字键盘会弹出 但当类型 密码 时 数字键盘不会弹出 并且我无法 或
  • Android 的自定义类加载器?

    我正在编写一个仪器库 我想在桌面和移动设备 Android 上使用它 它的功能是 公开一个带有单个参数的 main 即目标类的 main 安装一个类加载器 在加载所有类时拦截它们并对其进行检测 Like so Expects args 0
  • vscode 中使用 antd 框架的按钮提示

    我在 vscode 中使用 antd 的按钮和输入 但它在按钮上显示错误 我想知道为什么 我试过输入法 没有显示错误 如图所示 import React from react import styles from index css imp
  • 在移动 Safari 中检测 iOS5(首选 JavaScript)

    iOS5 中引入的新固定定位损坏了我的网络应用程序 我需要一种方法来检测 iOS5 用户 如何检测iOS5 浏览器代理字符串是什么 首选 JavaScript 谢谢 从SO问题来看 iOS 5 用户代理字符串是什么 https stacko
  • 移动后端API密钥的安全性

    假设我正在开发一个调用 API 服务器的移动应用程序 API 服务器由 API 密钥保护 我无法在移动应用程序中对 API 密钥进行硬编码 因为它可能会被盗 如何保护 API 密钥 假设我正在开发一个调用 API 服务器的移动应用程序 AP
  • jquery 移动列表视图样式

    我正在尝试设置列表视图控件的样式 但遇到问题 当我将图像宽度设置为 40px 如标记中所示 时 我似乎无法让内容正确地向左对齐 IE 我不想要图像和文本之间的间隙 这是我的标记 ul class ui listview li class u
  • 如何在 Xamarin.Forms 中设置不透明度动画

    我想知道如何对屏幕上可见元素的不透明度进行动画处理 例如 对于 Entry 我得到了这个 this Animate d gt Debug WriteLine anim d Username Opacity AnimationTime d A
  • 将 Yup 验证错误转换为可用对象

    Problem 我有一个 formik 表单 需要有 2 个不同的验证模式 具体取决于用户使用哪个按钮提交 我看到有些人说使用状态来决定哪个 但我想避免使用状态 因为在这种情况下感觉不对 我看过是的文档 https www npmjs co
  • 将 WebGL 应用程序部署为本机 iOS 或 Android 应用程序?

    有谁知道如何将 WebGL 应用程序部署为本机 iOS 或 Android 应用程序 商业中间件是可以接受的 但开放项目会更好 谢谢 作为 Joris 答案的延伸 这似乎是基于内森 德弗里斯的作品 http atnan com blog 2
  • Swiper 幻灯片 - 像 Airbnb Slider 一样显示上一张/下一张幻灯片的结束/开始?

    上面是滑块Airbnb 有没有办法获得类似的效果Swiper http idangero us swiper api 对于第一张幻灯片 左侧有一个空白区域并开始 下一张幻灯片的内容 对于中间幻灯片 有上一张和一张的开始和结束位置 下一张幻灯
  • 如何减少 Ionic Cordova 项目启动持续时间?

    我做了一个离子科尔多瓦项目 但发布到Android手机后 根据手机类型 我们的程序持续时间约为 10 20 秒 当我搜索这个问题时 人们说这是因为 启动画面持续时间 离子启动画面未加载 https stackoverflow com que
  • 应该如何在 Ant Design Upload 组件中设置 customRequest 以使用 XMLHttpRequest?

    我的组件一团糟 现在我传递了一个函数 我已经尝试了一百万件事但无法让它工作 export default class DatafileUpload extends Component initialState fileUploading f
  • 确定视口或“标准”浏览器的最佳方法

    所以 现在我们都知道 iOS 移动 Safari 使用视口 Android 浏览器也是如此 而不是 标准 浏览器窗口 这会导致问题overflow hidden and position fixed 不幸的是 iPad 的情况也是如此 我想
  • 如何在Vite配置中更改antd主题?

    是一个由Vite和React antd组成的项目 我想在 vite config ts 中动态处理 antd 主题 如果您能告诉我如何修改 React 组件中的 less modifyVars 值 我将不胜感激 这是当前屏幕 光状态 htt
  • Phonegap从Java代码获取本地存储值?

    我已经使用phonegap在客户端保存了数据本地存储 http docs phonegap com en 1 2 0 phonegap storage storage md html现在我想用java代码访问保存的数据 这可能吗 我怎样才能
  • 是的/Formik 异步验证与去抖

    如何将去抖应用于下面的异步验证 代码来自 Yup 的 github https github com jquense yup mixedtestname string message string function test functio
  • TS2322 类型“string”不可分配给类型““left”| “中心”| “对”|不明确的'

    我是打字稿新手 我使用 ant design 4 0 版 我想向我的列添加对齐属性 但在表的 columns 属性中它显示此错误 我知道我应该将变量的类型与 AlignType 相匹配 但我不知道具体该怎么做 提前谢谢你们了 interfa
  • 根据上一个 Flutter 的选择更改下拉按钮的选择

    我有2个DropdownButtonFormField我在那里可以选择汽车 我需要根据用户从 DropdownButtonFormField 中的第一个选择中选择的汽车型号来更改按钮的第二个选择 即 如果用户在第一个选择中选择梅赛德斯 则在
  • Ant设计文件上传中使用customRequest

    我正在使用 Axios 来处理文件上传 我遇到显示文件上传进度的问题 我使用的文件上传视图是 图片卡 HTML
  • “无法实例化活动”错误

    我的一个 Android 应用程序拥有大约 100 000 个用户 每周大约 10 次 我会通过 Google 的市场工具向我报告以下异常情况 java lang RuntimeException Unable to instantiate

随机推荐