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. 深入理解 EF Core:EF Core 写入数据时发生了什么?

    阅读本文大概需要 14 分钟. 原文:https://bit.ly/2C67m1C 作者:Jon P Smith 翻译:王亮 声明:我翻译技术文章不是逐句翻译的,而是根据我自己的理解来表述的.其中可能 ...

  2. Python学习日志-01

    一.使用入门 (1)问答环节 人们为何使用Python: 软件质量高:Python更注重可读性.一致性和软件质量,这将其与脚本语言世界中的其他工具区别开来.因为代码的设计致力于可读性,因此比起传统脚本 ...

  3. Docker精华 ,超全文档!

    我们的口号是:再小的帆也能远航,人生不设限!!    学习规划:继续上篇 <Docker入门>https://www.cnblogs.com/dk1024/p/13121389.html  ...

  4. debug PostgreSQL 9.6.18 using Eclipse IDE on CentOS7

    目录 debug PostgreSQL 9.6.18 using Eclipse IDE on CentOS7 1.概览 2.建立用户 3.编译postgre 4.启动Eclipse 5.设置环境变量 ...

  5. Mac OS 生成 icon 和 ico 文件

    [本文版权归微信公众号"代码艺术"(ID:onblog)所有,若是转载请务必保留本段原创声明,违者必究.若是文章有不足之处,欢迎关注微信公众号私信与我进行交流!] 1. 生成 IC ...

  6. 线程基础9-quene讲解

    在学习Java 多线程并发开发过程中,了解到DelayQueue类的主要作用:是一个无界的BlockingQueue,用于放置实现了Delayed接口的对象,其中的对象只能在其到期时才能从队列中取走. ...

  7. 内存节省机制C演示

    编写代码实质是通过指令对计算机内存进行操作,计算机的硬件设备往往十分有限,尤其是内存.如何使有限的存储空间利用效率达到最大,成为了代码优化首先要考虑的事情. 比如,输入三个数比较大小并输出最小值.下面 ...

  8. linux查看当前目录下,各文件夹大小

    du -lh --max-depth=1

  9. nginx 代理post请求做负载均衡

    项目中遇到nginx代理post请求nodejs服务.但是一直404.发现好像是nginx重定向的时候将post请求变成了get请求.上配置: # 负载均衡服务器配置 upstream pdf_ups ...

  10. SQL基础随记2 视图 存储过程

    SQL基础随记2 视图 存储过程   View CREATE/ALTER/DROP VIEW ViewName as SELECT(...) 可以在视图的基础上继续创建视图,即,将之前创建的视图当做表 ...