Evernote Export

首先。vue 的数据流是双向的,而 react 的数据流是单向的。
这意味着什么?
这意味着,vue 中,子组件可以用 emit 把数据更新传给父组件。而 react 中, 需要通过父组件把回调函数传给子组件实现类似功能。
 
为什么要说这个?因为框架的设计会影响到组件库的设计。组件库的设计必须配合框架。
 
我们回忆一下, antd3 中表单是怎么用的?
我们需要传入 onSubmit 回调函数,去做表单提交操作。
为什么需要传入这玩意?因为正如上面所说,需要通过父组件把回调函数传给子组件实现类似功能。
当我们使用 Form 组件的时候,页面就是父组件。Form 就是子组件。
我们把在页面文件里写的方法传给 Form 的 onSubmit ,它把这个方法绑定在表单的原生提交事件上,实现以上功能。
 

 
我们来看一下 antd3 的 Form.create。
 
Form.create(options)(Component)#
使用方式如下:
class CustomizedForm extends React.Component {}
 
CustomizedForm = Form.create({})(CustomizedForm);
 
然后,我们来看一下 antd3 的 this.props.form.getFieldDecorator(id, options)。

this.props.form.getFieldDecorator(id, options)#

经过getFieldDecorator包装的控件,表单控件会自动添加value(或valuePropName指定的其他属性)onChange(或trigger指定的其他属性),数据同步将被 Form 接管,这会导致以下结果:
你不再需要也不应该用onChange来做同步,但还是可以继续监听onChange等事件。
你不能用控件的valuedefaultValue等属性来设置表单域的值,默认值可以用getFieldDecorator里的initialValue。
你不应该用setState,可以使用this.props.form.setFieldsValue来动态改变表单值。
 
源码:
 
getFieldDecorator: function getFieldDecorator(name, fieldOption) {
var _this2 = this;
 
 
var props = this.getFieldProps(name, fieldOption);
return function (fieldElem) {
// We should put field in record if it is rendered
_this2.renderFields[name] = true;
 
 
var fieldMeta = _this2.fieldsStore.getFieldMeta(name);
var originalProps = fieldElem.props;
if (process.env.NODE_ENV !== 'production') {
var valuePropName = fieldMeta.valuePropName;
(0, _warning2['default'])(!(valuePropName in originalProps), '`getFieldDecorator` will override `' + valuePropName + '`, ' + ('so please don\'t set `' + valuePropName + '` directly ') + 'and use `setFieldsValue` to set it.');
var defaultValuePropName = 'default' + valuePropName[0].toUpperCase() + valuePropName.slice(1);
(0, _warning2['default'])(!(defaultValuePropName in originalProps), '`' + defaultValuePropName + '` is invalid ' + ('for `getFieldDecorator` will set `' + valuePropName + '`,') + ' please use `option.initialValue` instead.');
}
fieldMeta.originalProps = originalProps;
fieldMeta.ref = fieldElem.ref;
return _react2['default'].cloneElement(fieldElem, (0, _extends6['default'])({}, props, _this2.fieldsStore.getFieldValuePropValue(fieldMeta)));
};
},
 
fieldElem 就是我们传进入的表单DOM。
props 是传进去的各种选项(比如表单验证)处理后的东西。
fieldMeta 是把传进去的表单名(比如userName passWord)处理后的东西。
 
getFieldMeta:
function getFieldMeta(name) {
this.fieldsMeta[name] = this.fieldsMeta[name] || {};
return this.fieldsMeta[name];
}
 
getFieldValuePropValue:
function getFieldValuePropValue(fieldMeta) {
var name = fieldMeta.name,
getValueProps = fieldMeta.getValueProps,
valuePropName = fieldMeta.valuePropName;
 
 
var field = this.getField(name);
var fieldValue = 'value' in field ? field.value : fieldMeta.initialValue;
if (getValueProps) {
return getValueProps(fieldValue);
}
return (0, _defineProperty3['default'])({}, valuePropName, fieldValue);
}
 
cloneElement 是 react 方法。
 
总的来说做了两件事:
  • 把数据放入 Form.state ,便于之后的各种处理。
  • 把传进去的 DOM 进行混入与克隆。
 

 
而在 antd4 中——
 
 
v4 的 Form 不再需要通过Form.create()创建上下文。Form 组件现在自带数据域,因而getFieldDecorator也不再需要,直接写入 Form.Item 即可:
Form 自带表单控制实体,如需要调用 form 方法,可以通过Form.useForm()创建 Form 实体进行操作:
 
那么,什么叫自带数据域
看源码:
ant-design-master\components\form\Form.tsx:
return (
<SizeContextProvider size={size}>
<FormContext.Provider value={formContextValue}>
<FieldForm
id={name}
{...restFormProps}
onFinishFailed={onInternalFinishFailed}
form={wrapForm}
className={formClassName}
/>
</FormContext.Provider>
</SizeContextProvider>
);
 
很容易得知 FieldForm 是核心。
 
rc-field-form\es\Form.js:
return React.createElement(Component, Object.assign({}, restProps, {
onSubmit: function onSubmit(event) {
event.preventDefault();
event.stopPropagation();
formInstance.submit();
}
}), wrapperNode);
 
 
参数 Component 是元素名称、wrapperNode 是子元素DOM(常说成children)。
 
Component:
Component = _ref$component === void 0 ? 'form' : _ref$component,
 
wrapperNode:
var wrapperNode = React.createElement(_FieldContext.default.Provider, {
value: formContextValue
}, childrenNode);
 
_FieldContext 是存放警告信息的,如果一切正常就什么也不做。
 
childrenNode:
var childrenNode = children;
children = _ref.children,
 
我们发现:
  • 当声明 Form 的时候,会渲染 Form 元素。
  • 对于子元素基本上就是什么也不做。
 
为什么呢?
因为还有 Form.Item 。
 
ant-design-master\components\form\FormItem.tsx:
return (
<Row
className={classNames(itemClassName)}
style={style}
key="row"
{...omit(restProps, [
'colon',
'extra',
'getValueFromEvent',
'getValueProps',
'hasFeedback',
'help',
'htmlFor',
'id', // It is deprecated because `htmlFor` is its replacement.
'initialValue',
'isListField',
'label',
'labelAlign',
'labelCol',
'normalize',
'preserve',
'required',
'validateFirst',
'validateStatus',
'valuePropName',
'wrapperCol',
])}
>
{/* Label */}
<FormItemLabel htmlFor={fieldId} required={isRequired} {...props} prefixCls={prefixCls} />
{/* Input Group */}
<FormItemInput
{...props}
{...meta}
errors={mergedErrors}
prefixCls={prefixCls}
onDomErrorVisibleChange={setDomErrorVisible}
validateStatus={mergedValidateStatus}
>
<FormItemContext.Provider value={{ updateItemErrors: updateChildItemErrors }}>
{baseChildren}
</FormItemContext.Provider>
</FormItemInput>
</Row>
);
 
  • baseChildren 只有出错了才会出现,不用管。
  • Form.Item 一定会渲染 Col.
 
关键是 FormItemLabel 和 FormItemInput ,他们都会接收所有的 props 。
 
ant-design-master\components\form\FormItemLabel.tsx:
return (
<Col {...mergedLabelCol} className={labelColClassName}>
<label
htmlFor={htmlFor}
className={labelClassName}
title={typeof label === 'string' ? label : ''}
>
{labelChildren}
</label>
</Col>
);
 
  • 会渲染 Col.
  • 会渲染 label 元素。文字信息会放在 labelChildren 里面。
 
 
 
ant-design-master\components\form\FormItemInput.tsx:
return (
<FormContext.Provider value={subFormContext}>
<Col {...mergedWrapperCol} className={className}>
<div className={`${baseClassName}-control-input`}>
<div className={`${baseClassName}-control-input-content`}>{children}</div>
{icon}
</div>
<CSSMotion
motionDeadline={500}
visible={visible}
motionName="show-help"
onLeaveEnd={() => {
onDomErrorVisibleChange(false);
}}
motionAppear
removeOnLeave
>
{({ className: motionClassName }: { className: string }) => {
return (
<div className={classNames(`${baseClassName}-explain`, motionClassName)} key="help">
{memoErrors.map((error, index) => (
// eslint-disable-next-line react/no-array-index-key
<div key={index}>{error}</div>
))}
</div>
);
}}
</CSSMotion>
{extra && <div className={`${baseClassName}-extra`}>{extra}</div>}
</Col>
</FormContext.Provider>
);
 
children 就是某个表单元素,比如 Input 。
 
那么,表单的数据域到底存在于什么地方?它是怎么被声明的?
这必须说到两个东西: createContext useContext.
 
先看这两个东西在 antd4 中是如何被使用的吧。
 
ant-design-master\components\form\FormItem.tsx:
import { FormContext, FormItemContext } from './context';
 
const { name: formName } = React.useContext(FormContext);
const { updateItemErrors } = React.useContext(FormItemContext);
 
ant-design-master\components\form\context.tsx:
export const FormContext = React.createContext<FormContextProps>({
labelAlign: 'right',
vertical: false,
itemRef: (() => {}) as any,
});
 
export interface FormItemContextProps {
updateItemErrors: (name: string, errors: string[]) => void;
}
 
那么,Context 是个什么玩意?其实,它是 react 的一个机制
官方介绍已经说的很清楚了——
 
在一个典型的 React 应用中,数据是通过 props 属性自上而下(由父及子)进行传递的,但这种做法对于某些类型的属性而言是极其繁琐的(例如:地区偏好,UI 主题),这些属性是应用程序中许多组件都需要的。Context 提供了一种在组件之间共享此类值的方式,而不必显式地通过组件树的逐层传递 props。
 
所以答案已经呼之欲出了。
表单的数据域会存在,而且不需要声明。
因为它用的是 Context ,它没有也不需要独立的数据管理,表单容器的数据变化会直接反映到表单

antd4 源码学习 :表单的更多相关文章

  1. ddms(基于 Express 的表单管理系统)源码学习

    ddms是基于express的一个表单管理系统,今天抽时间看了下它的代码,其实算不上源码学习,只是对它其中一些小的开发技巧做一些记录,希望以后在项目开发中能够实践下. 数据层封装 模块只对外暴露mod ...

  2. ABP框架源码学习之修改默认数据库表前缀或表名称

    ABP框架源码学习之修改默认数据库表前缀或表名称 1,源码 namespace Abp.Zero.EntityFramework { /// <summary> /// Extension ...

  3. [数据结构1.2-线性表] 动态数组ArrayList(.NET源码学习)

    [数据结构1.2-线性表] 动态数组ArrayList(.NET源码学习) 在C#中,存在常见的九种集合类型:动态数组ArrayList.列表List.排序列表SortedList.哈希表HashTa ...

  4. [数据结构-线性表1.2] 链表与 LinkedList<T>(.NET 源码学习)

    [数据结构-线性表1.2] 链表与 LinkedList<T> [注:本篇文章源码内容较少,分析度较浅,请酌情选择阅读] 关键词:链表(数据结构)    C#中的链表(源码)    可空类 ...

  5. Java集合专题总结(1):HashMap 和 HashTable 源码学习和面试总结

    2017年的秋招彻底结束了,感觉Java上面的最常见的集合相关的问题就是hash--系列和一些常用并发集合和队列,堆等结合算法一起考察,不完全统计,本人经历:先后百度.唯品会.58同城.新浪微博.趣分 ...

  6. spring源码学习——spring整体架构和设计理念

    Spring是在Rod Johnson的<Expert One-On-One J2EE Development and Design >的基础上衍生而来的.主要目的是通过使用基本的java ...

  7. 移动端触摸、点击事件优化(fastclick源码学习)

    移动端触摸.点击事件优化(fastclick源码学习) 最近在做一些微信移动端的页面,在此记录关于移动端触摸和点击事件的学习优化过程,主要内容围绕fastclick展开.fastclick githu ...

  8. Qt Creator 源码学习笔记04,多插件实现原理分析

    阅读本文大概需要 8 分钟 插件听上去很高大上,实际上就是一个个动态库,动态库在不同平台下后缀名不一样,比如在 Windows下以.dll结尾,Linux 下以.so结尾 开发插件其实就是开发一个动态 ...

  9. async-validator 源码学习笔记(二):目录结构

    上一篇文章<async-validator 源码学习(一):文档翻译>已经将 async-validator 校验库的文档翻译为中文,看着文档可以使用 async-validator 异步 ...

随机推荐

  1. web scraper无法解决爬虫问题?通通可以交给python!

    今天一位粉丝的需求所涉及的问题值得和大家分享分享~~~ 背景问题 是这样的,他看了公号里的关于web scraper的系列文章后,希望用它来爬取一个网站搜索关键词后的文章标题和链接,如下图 按照教程, ...

  2. selenium自动化操作

    在前面爬虫的相关介绍中,我们介绍了如何抓取静态页面信息.但是,在实际的网页浏览过程中,我们可能会经常碰到各种需要进行交互的操作,典型的如输入信息.点击按钮之类. 对于这种场景,之前的静态页面操作方式已 ...

  3. 第一章:开始启程-你的第一行Android代码

    Android 系统为开发者提供了什么? 四大组件 活动(Activity):界面 服务(Service):后台默默运行 广播接收器(Broadcast Receiver):接收.发送广播消息 内容提 ...

  4. SpringMVC 学习笔记(四)

    41. 尚硅谷_佟刚_SpringMVC_返回JSON.avi SpringMVC中使用@ResponseBody注解标注业务方法,将业务方法的返回值做成json输出给页面 导包: 除了一些sprin ...

  5. JAVA基础你需要知道的几点

    一.关于变量 变量可以看成可操作的存储空间,有如下三种: 局部变量:定义在方法或语句块内部,必须先声明初始化才能使用:生命周期从声明位置开始到方法或语句块执行完毕. 成员变量(实例变量):定义在方法外 ...

  6. springboot + rabbitmq 用了消息确认机制,感觉掉坑里了

    本文收录在个人博客:www.chengxy-nds.top,技术资源共享,一起进步 最近部门号召大伙多组织一些技术分享会,说是要活跃公司的技术氛围,但早就看穿一切的我知道,这 T M 就是为了刷KPI ...

  7. CSS中那些必须掌握的概念

    一.盒子模型 1.什么是盒子模型 css盒模型本质上是一个盒子,封装周围的html元素,它包括:外边距(margin).边框(border).内边距(padding).实际内容(content)四个属 ...

  8. 浅谈bfs

    广搜(bfs) 定义 广度优先算法,简称BFS.是一种图形搜索演算法,简单的说,BFS是从根节点开始,沿着树的宽度遍历树的节点,如果发现目标,终止. 与dfs的相似之处与不同 结合深搜理解 相同点:都 ...

  9. 部署LNMP环境——Yum安装(113资讯网)

    Yum安装优点:安装东西,方便快捷,特别是不用考虑包依赖 教程(Centos做演示,其他系统大同小异): 1.更换国内Yum源: [root@root xx ]# rm -rf /etc/yum.re ...

  10. 02 . 分布式存储之FastDFS 高可用集群部署

    单节点部署和原理请看上一篇文章 https://www.cnblogs.com/you-men/p/12863555.html 环境 [Fastdfs-Server] 系统 = CentOS7.3 软 ...