实现antd中Form、Form.Item组件

初始化项目

使用create-react-app初始化项目后创建MyRcFieldForm文件,代码如下

import React, { Component, useEffect } from 'react';import Form, { Field } from 'rc-field-form';
//import Form, { Field } from './components/my-rc-field-form/';
import Input from './components/Input';const nameRules = { required: true, message: '请输入姓名!' };
const passworRules = { required: true, message: '请输入密码!' };export default function MyRCFieldForm(props) {const [form] = Form.useForm();const onFinish = (val) => {console.log('onFinish', val); //sy-log};// 表单校验失败执行const onFinishFailed = (val) => {console.log('onFinishFailed', val); //sy-log};useEffect(() => {console.log('form', form); //sy-logform.setFieldsValue({ username: 'default' });}, []);return (<div><h3>MyRCFieldForm</h3><Form form={form} onFinish={onFinish} onFinishFailed={onFinishFailed}><Field name='username' rules={[nameRules]}><Input placeholder='input UR Username' /></Field><Field name='password' rules={[passworRules]}><Input placeholder='input UR Password' /></Field><button>Submit</button></Form></div>);
}

Input组件代码

import React from 'react';const Input = (props) => {return <input {...props} />;
};
class CustomizeInput extends React.Component {constructor(props) {super(props);this.state = {};}render() {const { value = '', ...otherProps } = this.props;return (<div style={{ padding: 10 }}><Input style={{ outline: 'none' }} value={value} {...otherProps} /></div>);}
}export default CustomizeInput;

上面文件中的Form,Field组件是从rc-field-form库中引入的,接下来我们要自己实现这两个组件。

原理以及实现思路

让我们先来回顾一下antd4.0中表单组件的使用方法,首先使用Form组件包裹自组件,给Form组件可以添加onFinish,onFieldsChange等事件,在这些事件中可以监听到表单的改变获取到表单值然后做相应的处理。我们还可以使用Form.useForm方法获取表单实例,通过该实例中的方法(setFieldsValue,getFieldsValue,validate)我们可以获取、设置、校验表单值。然后使用Form.Item组件包裹表单组件,给Form.Item组件添加上name,rules等属性。然后当表单被输入是我们可以获取到表单的内容。

const Demo = () => {const [form] = Form.useForm();const handleChange = values => console.log(values);return (<Form form={form} onChange={handlChange}><Form.Item name="usename" ><Input /></Form.Item></Form>)
}

实现思路:首先我们需要将表单组件的输入值存储到一个公共的位置 store,然后,暴露出修改方法给表单组件,当表单组件InputonChange事件触发后调用修改方法(setFieldsValue)将新的值更新到store,然后重新触发组件渲染将视图同步。我们通过Form.useForm创建store的实例,然后通过React中的context将实例传递给各个Form.Item组件。在Form.Item组件中我们需要为表单组件Input实现onChange事件,将改变通过context中的实例方法更新到store当中,最后重新渲染自己实现视图同步。

搭出Form、Field组件,useForm自定义hook的架子

  • context文件
const FormContext = React.createContext();
export default FormContext;
  • Form
import FormContext from './context'
const Form = ({children, form}) => {return (<FormContext.Provider value={form}><form>{children}</form></FormContext.Provider>)
}
  • Field
import FormContext from './context'
class Field extends Component {static contextType = FormContextgetControled = () => {const {name} = this.props;return {value: '',onChange: e => {console.log(e.target.value}}}render() {const {children} = this.props;const newChildren = React.cloneElement(children, {...this.getControled()})return newChildren}
}
  • useForm
class FieldStore {constructor() {this.store = {};}getFieldValue = (name) => {return this.store[name]}getFieldsValue = () => {return this.store}setFieldsValue = (newValues) => {this.store = {...this.store,...newValues}} setFieldValue = (newValues) => {this.store = {...this.store,...newValues}} validate = () => {}getForm = () => {return {getFieldValue: this.getFieldValue,getFieldsValue: this.getFieldsValue,setFieldsValue: this.setFieldsValue,setFieldValue: this.setFieldValue,validate: this.validate,}}
}
const useForm = (form) => {const formRef = React.useRef();if (!formRef.current) {if (form) {formRef.current = form} else {formRef.current = new FieldStore()}}return [formRef.current]
}
export default useForm;

完善功能

  • Field组件
    上面的Field组件还没有实现获取到store里面的value以及当chang事件触发后将值更新到store里面最后自己更新自己实现视图同步的功能,接下来我们一步一步来实现。
    我们已经绑定了context,于是我们可以从表单实例里面获取到操作store的方法,首先在getControled方法中通过getFieldsValue方法获取到该组件组件对应的值赋值给value,然后在onChange事件里面将e.target.value通过setFieldsValue更新store。代码如下
     getControled = () => {const {name} = this.props;const {getFieldValue, setFieldsValue} = this.context;return {value: getFieldValue(name),onChange: e => {setFieldsValue(e.target.value)}}}

接下来还剩更新自己,我们可以在store里面维护一个实例数组entities获取到所有的表单组件实例,然后在setFieldsValue方法调用时拿到对应的实例调用他的更新方法将其更新,组件自己更新时我们使用到了组件的foruceUpdate方法,由于只需要注册实例一次,我们在Field组件的componentWillMount生命周期当中将该组件的实例也就是this加到entities里面。这里还有一个问题不要忘了,当该组件被隐藏时需要将该其实例从entities里面删掉,所以我们还要在组件的componentWillUnmount方法里取消注册。是不是很简单呢,请看代码~

  • useForm
 constructor() {this.store = {};this.entities = [];}registerEntity = (entity) => {this.entities.push(entity);return () => {const name = entity.props.name;this.entities = this.entities.filter(item => item !== entity)delete this.store[name]}}setFieldsValue = (newValues) => {this.store = {...this.store,...newValues}this.entities.forEach(entity => {const {name} = entity.propsif (newValues.includes(name)) {entity.fourceUpdate()}})}
  • Field
componentWillMount() {const {registerEntity} = this.context;this.cancelRegister = registerEntity(this);
}componentWillUnmount() {this.cancelRegister();
}fourceUpdate() {this.forceUpdate();
}

到这里我们的表单组件已经基本可以使用了,校验功能我们只需要在validate方法里面拿到对应的校验规则rulesstore里面的值进行校验并返回对应的信息就可以了,这里就不写了。
最后我们来实现一下Form组件的提交方法,antd中的Form组件上有很多的事件例如onFinish, onFinishFailed,onFieldsChange等,我们在store里面再维护一个callbacks对象来存储这些事件。

  • useForm
 constructor() {this.store = {};this.entities = [];this.callbacks = {}}setCallback = callback => {this.callbacks = {...this.callbacks,...callback};};submit = () => {console.log("this.", this.fieldEnetities); //sy-loglet err = this.validate();// 在这里校验 成功的话 执行onFinish ,失败执行onFinishFailedconst {onFinish, onFinishFailed} = this.callbacks;if (err.length === 0) {// 成功的话 执行onFinishonFinish(this.getFiledsValue());} else if (err.length > 0) {// ,失败执行onFinishFailedonFinishFailed(err);}};
  • Form
export default function Form({form, children, onFinish, onFinishFailed}) {const [formInstance] = useForm(form);formInstance.setCallback({onFinish,onFinishFailed});return (<formonSubmit={event => {event.preventDefault();formInstance.submit();}}><FieldContext.Provider value={formInstance}>{children}</FieldContext.Provider></form>);
}

以上就是antd4中Form、Form.Item组件的基本实现啦,由于上面的代码是我写该文章时现写的可能有错误,下面是完整代码~

完整代码

  • Form
import React from 'react';
import FieldContext from './context';
import { useForm } from './useForm';
const Form = ({ children, form }) => {const [formInstance] = useForm(form);const onSubmit = (e) => {e.preventDefault();formInstance.submit();};return (<form onSubmit={onSubmit}><FieldContext.Provider value={formInstance}>{children}</FieldContext.Provider></form>);
};export default Form;
  • Field
import React, { useEffect } from 'react';
import FieldContext from './context';
class Field extends React.PureComponent {static contextType = FieldContext;componentWillMount() {const { registerEneity } = this.context;this.cancelRegister = registerEneity(this);}componentWillUnmount() {this.cancelRegister();}filedFourceUpdate() {this.forceUpdate();}getControled = () => {const { getFieldValue, setFieldsValue, getFieldsValue } = this.context;const { name } = this.props;return {value: getFieldValue(name),onChange: (e) => {setFieldsValue({ [name]: e.target.value });console.log(getFieldsValue());},};};render() {const { children } = this.props;const newChildren = React.cloneElement(children, { ...this.getControled() });return <>{newChildren}</>;}
}export default Field;
  • useForm
import React, { useEffect } from 'react';class formStore {constructor() {this.store = {};this.entities = [];this.callbacks = {};}registerEneity = (entity) => {this.entities.push(entity);return () => {this.entities = this.entities.filter((item) => item !== entity);delete this.store[entity.props.name];};};getFieldsValue = () => {return this.store;};setFieldsValue = (newVals) => {this.store = {...this.store,...newVals,};this.entities.forEach((entity) => {const name = entity.props.name;if (Object.keys(newVals).includes(name)) entity.filedFourceUpdate();});};getFieldValue = (name) => {console.log(this);return this.store[name];};setFieldValue = () => {console.log('setFieldsValue');};validate = () => {};setCallbacks = (callbacks) => {this.callbacks = {...this.callbacks,...callbacks,};};submit = () => {console.log(this.store);};getForm = () => {return {getFieldsValue: this.getFieldsValue,setFieldsValue: this.setFieldsValue,getFieldValue: this.getFieldValue,setFieldValue: this.setFieldValue,validate: this.validate,registerEneity: this.registerEneity,setCallbacks: this.setCallbascks,submit: this.submit,};};
}const useForm = (form) => {const formInstance = React.useRef();if (!formInstance.current) {if (form) {formInstance.current = form;} else {const store = new formStore();formInstance.current = store.getForm();}}return [formInstance.current];
};export { useForm };

你以为这样就完了吗,不不不。接下来我们看简单看一看antd3中Form组件的实现

antd3中的表单组件

antd3中使用高阶组件的方式实现Form表单,我们简单说一下高阶组件的实现思路,我们使用一个createForm高阶组件,它返回一个类组件,像上面一样我们需要一个地方存储所有的表单值以及创建修改值的方法,我们在createForm里面用this.state存储表单值,并使用getFieldDecorator方法为input组件添加onChange事件,以及valuevaluestate中获取,这样通过setState方法改变值时视图也会更新,当input值改变时通过setState将改变更新到state上面。setFieldsValue、getFieldsValue这些方法很简单就不说了。
代码如下

import React, {Component} from "react";export default function createForm(Cmp) {return class extends Component {constructor(props) {super(props);this.state = {};this.options = {};}handleChange = e => {const {name, value} = e.target;this.setState({[name]: value});};getFieldDecorator = (field, option) => InputCmp => {this.options[field] = option;return React.cloneElement(InputCmp, {name: field,value: this.state[field] || "",onChange: this.handleChange});};setFieldsValue = newStore => {this.setState(newStore);};getFieldsValue = () => {return this.state;};validateFields = callback => {let err = [];// 校验 检验规则 this.options// 校验的值是this.statefor (let field in this.options) {// 判断state[field]是否是undefined// 如果是undefind err.push({[field]: 'err})if (this.state[field] === undefined) {err.push({[field]: "err"});}}if (err.length === 0) {// 校验成功callback(null, this.state);} else {callback(err, this.state);}};getForm = () => {return {form: {getFieldDecorator: this.getFieldDecorator,setFieldsValue: this.setFieldsValue,getFieldsValue: this.getFieldsValue,validateFields: this.validateFields}};};render() {return <Cmp {...this.props} {...this.getForm()} />;}};
}

使用代码

import React, {Component} from "react";
// import {createForm} from "rc-form";
import createForm from "../components/my-rc-form/";import Input from "../components/Input";const nameRules = {required: true, message: "请输入姓名!"};
const passworRules = {required: true, message: "请输入密码!"};@createForm
class MyRCForm extends Component {constructor(props) {super(props);// this.state = {//   username: "",//   password: ""// };}componentDidMount() {this.props.form.setFieldsValue({username: "default"});}submit = () => {const {getFieldsValue, validateFields} = this.props.form;// console.log("submit", getFieldsValue()); //sy-logvalidateFields((err, val) => {if (err) {console.log("err", err); //sy-log} else {console.log("校验成功", val); //sy-log}});};render() {console.log("props", this.props); //sy-log// const {username, password} = this.state;const {getFieldDecorator} = this.props.form;return (<div><h3>MyRCForm</h3>{getFieldDecorator("username", {rules: [nameRules]})(<Input placeholder="Username" />)}{getFieldDecorator("password", {rules: [passworRules]})(<Input placeholder="Password" />)}<button onClick={this.submit}>submit</button></div>);}
}export default MyRCForm;

实现antd中Form、Form.Item组件相关推荐

  1. 前端---antd中的日期选择组件

    React---使用antd中的DatePicker实现YearPicker 需求React 现有的组件类型 手动实现YearPicker 需求React 需要设计一个时间选择框,但是只需要选中某年即 ...

  2. antd中的form表单 initialValue导致数据不更新问题

    初步理解 : initialValue就是所谓的defaultValue,只会在第一次赋值的时候改变,却又有一些不同,因为 initialValue又会因其他改动而改变. 然而当获取的数据重新上来要渲 ...

  3. Antd中Form表单与Table表格联合的简易使用

    Form.Item 中使用 valuePropName 来指定子节点的值的属性,例如Table的数据源为dataSource,Switch的数据源是checked 在Table的columns中定义需 ...

  4. extjs 提交表单给php,JavaScript_Extjs学习笔记之二 初识Extjs之Form,Extjs中的表单组件是Ext.form.Basic - phpStudy...

    Extjs学习笔记之二 初识Extjs之Form Extjs中的表单组件是Ext.form.BasicForm,不过最简单最常用的是Ext.form.FormPanel控件,它继承自Panel,具有一 ...

  5. antd design(4.x)中的Form

    1.初始化表单数据 initialValues:表单默认值,只有初始化以及重置时生效. 代码示例如下: <Form layout="horizontal" form={for ...

  6. Django Form和ModelForm组件

    Form介绍 我们之前在HTML页面中利用form表单向后端提交数据时,都会写一些获取用户输入的标签并且用form标签把它们包起来. 与此同时我们在好多场景下都需要对用户的输入做校验,比如校验用户是否 ...

  7. Flutter修仙传第一章:从Form入手学会组件使用

    前言 话说代码武林中出了一套绝世神功,名为Flutter,Android,iOS,Web等各路大侠纷纷前往,一探究竟,临前一览才发觉这套神功和Android,iOS这两派功法有着相辅相成的无尽妙处,习 ...

  8. angular select2源码解析_Angular 组件库 NG-NEST 源码解析:Form 表单组件

    前言 NG-NEST介绍 今天我们来看一下 Form 表单组件是如何实现的: 功能分析 由不同的表单控件组成(输入框.选择器.单选框.多选框等) 控件的禁用.必填.正则验证等状态 标题和控件位置,局部 ...

  9. React+ant中的Form表单的刷新

    我们使用ant组件库的Form表单提交之后不刷新整个页面,表单中的内容有时候是不会刷新,如何解决这个问题? 首先定义一个[form]通过 Form.useForm 对表单数据域进行交互, const ...

  10. 在微信小程序中提交form表单

    怎么在微信小程序中提交form表单 发布时间:2021-04-07 16:26:31 来源:亿速云 阅读:1229 作者:Leah 栏目:web开发 活动:亿速云新用户活动,各类服务器大降价,满足绝大 ...

最新文章

  1. 量子计算机与人脑接口,量子信息科学:量子计算机、隐形传物与人脑量子运算...
  2. Paper1:HoPE: Horizontal Plane Extractor for Cluttered
  3. 前端项目课程3 jquery1.8.3到1.11.1有了哪些新改变
  4. 128条形码计算,利用Code 128字体实现条码打印
  5. 直方图均衡化算法 matlab,matlab程序_retinex_frankle_mccann+直方图均衡化_去雾算法
  6. ux和ui_如何为您的UX / UI设计选择正确的原型制作工具
  7. LightGBM中GBDT的实现
  8. Xamarin截取/删除emoji表情bug解决方案
  9. c语言文件读写_学生信息管理系统(C语言\单向链表\文件读写)
  10. java数组移除对象_如何从Java数组中删除对象?
  11. 《云计算:概念、技术与架构》一1.5 书写惯例
  12. 概率分布与马尔科夫链的关系讨论(上传费事)
  13. 【深度之眼Python基础+数据科学入门训练营】第八章 文件、异常和模块
  14. 另一条路去IOE:全内存数据库弯道超车
  15. SpringBoot+log4j2.xml读取application.yml属性值
  16. 100个冷笑话 越到后面越冷
  17. 深度学习(七)——图像验证码破解(数字加减验证码)
  18. Verilog中三态门(高阻态)的理解与例子
  19. IT搬砖员如何认识能力圈并如何突破自我
  20. 解码大数据视频营销 爱奇艺专场闪耀2013金投赏

热门文章

  1. 《给青年的十二封信》 朱光潜 (摘录)
  2. 通过物理地址查计算机,别人知道我查电脑的物理地址,怎么处理
  3. 这是一份值得你去查看的Android安全手册
  4. c语言 数据类型作业 答案,C语言-数据类型习题及答案
  5. python模拟浏览器访问企查查_python爬虫另辟蹊径绕过企查查的登录验证,我太冇财了...
  6. python中print是什么意思中文-python里print是什么意思
  7. notepad下载(官网进不去)
  8. 鸿蒙双系统怎么切换,苹果 Mac双系统如何切换?用Option键切换双系统的步骤分享...
  9. RxJava Observer与Subscriber的关系
  10. 订阅者Subscriber的编程实现