form表单在我们日常的开发过程中被使用到的概率还是很大的,比如包含了登录、注册、修改个人信息、新增修改业务数据等的公司内部管理系统。而在使用时这些表单的样式如高度、上下边距、边框、圆角、阴影、高亮等等都大同小异、大差不差且表单的功能基本相似,所以很有必要对这些表单来一个简单的封装。

其实,封装组件的意义我们在上一篇封装react antd的表格table组件中已经介绍过了,这里不再做过多的描述了,且在“封装react antd的表格table组件”一文中,我们也对相关技术作了简要介绍。接下来,再来介绍一下本次封装所用到的其他技术。

本次封装用到了useForm,官方解释:useForm是React Hooks的实现,只能用于函数组件。我们可以通过Form.useForm对表单数据域进行交互,如:setFieldsValue()、getFieldValue()、validateFields()、resetFields()等。

本次封装还用到了useEffect,它也是React Hooks的实现,相当于componentDidMount和componentDidUpdate以及componentWillUnmount这些生命周期的集合体,但需要注意react首次渲染和之后的每次渲染都会调用一遍传给useEffect的函数,但每次重新渲染都要执行一遍这些副作用函数,显然对于追求性能的我们来说是很不友好的,那么该咋办呢?useEffect给我们提供了第二个参数,可用于定义其依赖的所有变量。如果其中一个变量发生变化,则useEffect会再次运行,如果包含变量的数组为空,则在更新组件时useEffect不会再执行,因为它不会监听任何变量的变更。关于useEffect的介绍还有很多,可自行查阅API或资料,这里同样不再做过多的描述了。

本次封装所使用到的方法的介绍基本完毕,以下是组件封装的具体实现部分。

还是先贴一张最后实现的效果图:



1、所封装的antd form组件form.js

import React, { createElement, useEffect, useImperativeHandle } from 'react'
import PropTypes from 'prop-types'
import { Form, Input, Select, DatePicker, Button } from 'antd';
import { timestampToTime } from "@/utils"; const FormItem = Form.Item, { Password } = Input, { Option } = Select, h = createElement; const FormComponent = ({columns, data, cRef}) => {
//通过Form.useForm对表单数据域进行交互。useForm是React Hooks的实现,只能用于函数组件
const [form] = Form.useForm();
//cRef就是父组件传过来的ref
useImperativeHandle(cRef, () => ({
//getForm就是暴露给父组件的方法
getForm: () => form,
})); const layout = {
labelCol: { span: 4 },
wrapperCol: { span: 16 },
},
tailLayout = {
wrapperCol: { offset: 4, span: 16 },
}; //若有正则验证,则在所有的正则校验都通过后用来获取输入的数据,若没有正则校验,则直接获取输入的数据
const onFinish = values => {
values.date = timestampToTime(values.date).replace(' ', '')
console.log(values);
}; //重置要配合着const [form] = Form.useForm()以及form={form}
const onReset = () => {
form.resetFields();
}; //form表单的回显
useEffect(() => {
form.setFieldsValue(data)
}, []) const components = {
select: ({label, list = [], callback = () => {}}) => h(Select, {placeholder: label, onChange: v => callback(v)}, list.map(c => h(Option, {key: c.value, value: c.value}, c.label))),
input: ({label}) => <Input placeholder={label} />,
password: ({label}) => h(Password, {placeholder: label}),
datePicker: ({label}) => <DatePicker placeholder={label} format="YYYY-MM-DD" />,
} return (
<Form {...layout} form={form} onFinish={onFinish}>
{
columns.map(n => {
n.type = n.type ? n.type : 'input'
return <FormItem label={n.label} name={n.name} rules={n.rules} key={n.name}>
{components[n.type](n)}
</FormItem>
})
}
<FormItem {...tailLayout}>
<Button type="primary" htmlType="submit" style={{marginRight: '20px'}}>提交</Button>
<Button htmlType="button" onClick={onReset}>重置</Button>
</FormItem>
</Form>
)
} FormComponent.propTypes = {
columns: PropTypes.array.isRequired,
data: PropTypes.object,
cRef: PropTypes.object,
} export default FormComponent

2、时间戳格式化timestampToTime.js

export const timestampToTime = (timestamp, type) => {
let date = new Date(timestamp); //时间戳为10位需*1000,时间戳为13位的话不需乘1000
let Y = date.getFullYear() + '-';
let M = (date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1) + '-';
let D = date.getDate() < 10 ? '0' + date.getDate() + ' ' : date.getDate() + '';
let h = date.getHours() < 10 ? '0' + date.getHours() + ':' : date.getHours() + ':';
let m = date.getMinutes() < 10 ? '0' + date.getMinutes() + ':' : date.getMinutes() + ':';
let s = date.getSeconds() < 10 ? '0' + date.getSeconds() : date.getSeconds(); if (type == 'second') {
return Y + M + D + ' ' + h + m + s;
} else {
return Y + M + D
}
}

3、form组件的使用方法:

import React, { useState, useRef } from 'react'
import { Button } from 'antd';
import Form from './form'
import { timestampToTime } from "@/utils"; const FormComp = () => {
//useState只能放在组件内
let [isRequired, setRequired] = useState(true); const config = {
columns: [
{name: 'userName', label: '用户名', rules: [{ required: true, message: '请输入用户名' }]},
{name: 'password', label: '密码', type: 'password', rules: [{ required: true, message: '请输入密码' }]},
{name: 'confirmPwd', label: '确认密码', type: 'password', rules: [
{ required: true, message: '请再一次输入密码' },
({ getFieldValue }) => ({
validator(rule, value) {
//如果value为空,!value为true则直接返回Promise.resolve()就会提示“请再一次输入密码”
if (!value || getFieldValue('password') === value) {
return Promise.resolve();
}
return Promise.reject('两次密码输入不一致');
},
}),
]},
{name: 'email', label: '邮箱', rules: [
{ required: true, message: '请输入邮箱' },
{
validator:(_, value) => {
let reg = /^([a-zA-Z]|[0-9])(\w|\-)+@[a-zA-Z0-9]+\.([a-zA-Z]{2,4})$/;
if(!value || reg.test(value)){
return Promise.resolve();
} return Promise.reject('邮箱格式不正确');
}
}
]},
{name: 'gender', label: '性别', type: 'select', rules: [{ required: true, message: '请选择性别' }], list: [{value: 'male', label: '男'}, {value: 'female', label: '女'}], callback: res => onGenderChange(res)},
{name: 'highHeeled', label: '高跟鞋', rules: [{ required: isRequired, message: '请输入喜欢的高跟鞋' }]},
{name: 'exercise', label: '运动', rules: [{ required: true, message: '请输入喜欢的运动' }]},
{name: 'date', label: '日期', type: 'datePicker', rules: [{ required: true, message: '请输入日期' }]},
],
data: {
userName: '小坏',
gender: 'female',
},
} //切换性别时会显示隐藏高跟鞋的正则校验
const onGenderChange = value => {
switch (value) {
case "male":
setRequired(false);
break;
case "female":
setRequired(true);
break;
}
}; const childRef = useRef();
const getFormVal = () => {
const form = childRef.current.getForm();
//可以在验证后再获取值
form.validateFields().then(valid => {
valid.date = timestampToTime(valid.date).replace(' ', '')
console.log(valid)
}).catch((e) => {})
//也可以在不需要正则验证的地方直接通过这种方式来获取值
// console.log(form.getFieldValue())
} return <div>
<Form {...config} cRef={childRef} />
<Button type="primary" onClick={getFormVal} style={{marginLeft: '12%'}}>在组件外部获取form表单的值</Button>
</div>
} export default FormComp

在使用封装的form表单组件时,由于回显表单数据采用了useEffect方式,所以在切换性别select表单的过程中,就多次触发了useEffect中的副作用函数,这在我们肉眼可观察到的范围内虽然看不到这种变化,但确实是多次触发了回显表单数据的函数,这很显然是造成了性能的浪费,所以我就给useEffect的第二个参数设置了一个空数组,用来告诉useEffect没有数据更新,就不会再执行它里边的函数了。

最后再贴一下本次封装所用到的各个包的版本:

react: 16.8.6,

react-dom: 16.8.6,

react-router-dom: 5.0.0,

antd: 4.3.5,

@babel/core: 7.4.4,

babel-loader: 8.0.5

其实最主要的是react和antd的版本,其中antd4和antd3在form组件上的差异还是很大的,在其他组件上的差异也是很大的。

封装react antd的form表单组件的更多相关文章

  1. 封装Vue Element的form表单组件

    前两天封装了一个基于vue和Element的table表格组件,阅读的人还是很多的,看来大家都是很认同组件化.高复用这种开发模式的,毕竟开发效率高,代码优雅,逼格高嘛.虽然这两天我的心情很糟糕,就像& ...

  2. 封装react antd的upload上传组件

    上传文件也是我们在实际开发中常遇到的功能,比如上传产品图片以供更好地宣传我们的产品,上传excel文档以便于更好地展示更多的产品信息,上传zip文件以便于更好地收集一些资料信息等等.至于为何要把上传组 ...

  3. Vue + Element-ui实现后台管理系统(5)---封装一个Form表单组件和Table表格组件

    封装一个Form表单组件和Table组件 有关后台管理系统之前写过四遍博客,看这篇之前最好先看下这四篇博客.另外这里只展示关键部分代码,项目代码放在github上: mall-manage-syste ...

  4. Form( 表单) 组件

    本节课重点了解 EasyUI 中 Form(表单)组件的使用方法, 这个组件不依赖于任何组件.一. 加载方式表单组件只能在 JS 区域设置,首先定义一张表单.<form id="box ...

  5. 第二百二十一节,jQuery EasyUI,Form(表单)组件

    jQuery EasyUI,Form(表单)组件 学习要点: 1.加载方式 2.属性列表 3.事件列表 4.方法列表 本节课重点了解 EasyUI 中 Form(表单)组件的使用方法,这个组件不依赖于 ...

  6. Django form表单 组件

    目录 Django form表单 组件 Form 组件介绍 普通方式手写注册功能 使用form组件实现注册功能 Form 常用字段与插件 常用字段(必备) 字段参数(必备) 内置验证(必备) 自定义效 ...

  7. 【antd】form表单默认值设置

    问题: 在antd的form表单的api里面有个"initialValues"可以设置默认值.但是表单没有更新 <Form name="test" for ...

  8. react引用antd的form表单

    引用form是第三方插件ant插件,官网网址:https://ant.design/.用到的antd的版本是@2.0.1.form(https://ant.design/components/form ...

  9. antd做form表单的组件共用,利用mapPropsToFields填写默认值

    做单页应用,不管是用Vue还是React,或者其他,有一个重要的原则,就是:组件重用. 既然组件可以重用,那么当添加一个信息,和修改该信息的布局必然是一致的,这时候,最好的方法自然是利用同一个组件,在 ...

随机推荐

  1. pycharm控制台输出的日志全是红色的字体?

    问题:logging在pycharm控制台输出的日志的字体全是红色的,怎么办? 图片描述: 解决办法:设置 -> 搜索“Console” ->  结果:改完立马生效

  2. Spark 3.0 新特性 之 自适应查询与分区动态裁剪

    Spark憋了一年半的大招后,发布了3.0版本,新特性主要与Spark SQL和Python相关.这也恰恰说明了大数据方向的两大核心:BI与AI.下面是本次发布的主要特性,包括性能.API.生态升级. ...

  3. Git 提交、删除、切换命令

    1.将本地代码提交到远程仓库 [初始将文件修改上传到远程仓库] 初始化: git init 添加到暂存区: git  add . 提交到仓库: git commit -m 'first commit' ...

  4. emwin显示汉字使用vs studio仿真和使用keil编写烧录的不同

    我用emwin是在新唐的开发板上练习的,所有我就去官网下了开发板的资料,别的开发板应该也有对应的资料,这些软件网上应该很容易搜得到 然后用GUIBuilder构建一个界面,再用FontArchitec ...

  5. 让内层浮动的Div将外层Div撑开 -----清浮动

    清浮动的好处写多了都能体会到,解决高度塌陷, 一般情况下是要清除浮动的,不然会影响下面标签的排版. <div class="parent" style="width ...

  6. cmd 安装第三方库问题

    pip install 包名 -i http://pypi.douban.com/simple/ --trusted-host pypi.douban.com 一定要指定 信任豆瓣源,不然就算换了源 ...

  7. Python os.stat_float_times() 方法

    概述 os.stat_float_times() 方法用于决定stat_result是否以float对象显示时间戳.高佣联盟 www.cgewang.com 语法 stat_float_times() ...

  8. PHP fileowner() 函数

    定义和用法 fileowner() 函数返回指定文件的用户 ID(所有者). 如果成功,该函数返回用户 ID.如果失败,则返回 FALSE. 语法 fileowner(filename) 参数 描述 ...

  9. 7.9 NOI模拟赛 数列 交互 高精 字符串

    这是交互题 也是一个防Ak的题目 4个\(subtask\) 需要写3个不尽相同的算法. 题目下发了交互程序 所以调试的时候比较方便 有效防止\(CE\). 题目还有迷糊选手的点 数字位数为a 范围是 ...

  10. luogu P5826 【模板】子序列自动机 主席树 vector 二分

    LINK:子序列自动机 想了一些很有趣的做法. dp 容易看出 f[i][j]表示前i个数匹配了j个数的dp 不过复杂度很高. 贪心 容易想到匹配的时候每个数字尽量往前匹配 这样显然是最优的 复杂度Q ...