摘要:引言看过源码的都知道,其实是在一组组件的基础上进行了一层封装,本文主要解读组件的基础组件,另外会略过模式下的代码。解读源码首先要从自己最常用的或者感兴趣的入手,首先组件最主要的还是在这个装饰器入手。
引言
看过antd源码的都知道,antd其实是在一组react-componment组件的基础上进行了一层ui封装,本文主要解读antd组件Form的基础组件react-componment/form,另外会略过development模式下的warning代码。
Form.create解读源码首先要从自己最常用的或者感兴趣的入手,首先form组件最主要的还是在Form.create({options})这个装饰器入手。找到项目下的文件createForm.js,这个文件还是主要主要对createBaseForm.js文件进行了一层封装,提供了一些默认配置参数,下看查看createBaseForm.js里的createBaseForm方法,改方法主要是一个装饰器作用,包装一个高阶React组件,在props里注入一个值为formPropName(默认为form)变量,所有功能在这个变量里完成,主要内容如下
render() { const { wrappedComponentRef, ...restProps } = this.props; const formProps = { [formPropName]: this.getForm(), // 来在 formPropName默认为form,getForm方法来自`createForm.js` }; if (withRef) { formProps.ref = "wrappedComponent"; } else if (wrappedComponentRef) { formProps.ref = wrappedComponentRef; } const props = mapProps.call(this, { ...formProps, ...restProps, }); return; }
在装饰器初始化的时候,Form初始化了一个只属于该组件实例的store,用来存放当前Form组件的一些输入的数据,主要代码如下:
const fields = mapPropsToFields && mapPropsToFields(this.props); // mapPropsToFields来自于Form.create的配置参数,用来转化来自mobx或者redux等真正的store来源的value,以初始化该Form实例的fieldsStore this.fieldsStore = createFieldsStore(fields || {}); // createFieldsStore来自于文件`createFieldsStore.js`文件getFieldDecorator
柯里化函数,通过id与参数声明的输入,返回一个函数以输入组件为入参的函数,通过该函数声明的输入组件与表单Form双向数据绑定。
... getFieldDecorator(name, fieldOption) { const props = this.getFieldProps(name, fieldOption); // 初始化一个field return (fieldElem) => { const fieldMeta = this.fieldsStore.getFieldMeta(name); // 获取变化(Form的onChange)后的field数据 const originalProps = fieldElem.props; fieldMeta.originalProps = originalProps; // 输入组件初始化时保存的Prop fieldMeta.ref = fieldElem.ref; return React.cloneElement(fieldElem, { ...props, ...this.fieldsStore.getFieldValuePropValue(fieldMeta), // 获取prop属性 value }); }; } ...getFieldProps
查看函数 getFieldProps,主要用来初始化输入组件的props,将特定的函数缓存在内部,如onChange事件,另外初次保存field到store中
... getFieldProps(name, usersFieldOption = {}) { if (!name) { throw new Error("Must call `getFieldProps` with valid name string!"); } delete this.clearedFieldMetaCache[name]; const fieldOption = { name, trigger: DEFAULT_TRIGGER, valuePropName: "value", validate: [], ...usersFieldOption, // 用户输入,如rules,initialValue }; const { rules, trigger, validateTrigger = trigger, validate, } = fieldOption; const fieldMeta = this.fieldsStore.getFieldMeta(name); if ("initialValue" in fieldOption) { fieldMeta.initialValue = fieldOption.initialValue; } const inputProps = { ...this.fieldsStore.getFieldValuePropValue(fieldOption), // 获取输入组件的value,如果没有,返回initialValue ref: this.getCacheBind(name, `${name}__ref`, this.saveRef), }; if (fieldNameProp) { // 及value inputProps[fieldNameProp] = name; } const validateRules = normalizeValidateRules(validate, rules, validateTrigger); // 校验规则标准化 const validateTriggers = getValidateTriggers(validateRules); validateTriggers.forEach((action) => { if (inputProps[action]) return; inputProps[action] = this.getCacheBind(name, action, this.onCollectValidate); // 如果设置了输入校验rules,绑定onChange事件`this.onCollectValidate` }); // make sure that the value will be collect if (trigger && validateTriggers.indexOf(trigger) === -1) { inputProps[trigger] = this.getCacheBind(name, trigger, this.onCollect); // 如果没有绑定rules校验,绑定默认的onChange事件 } const meta = { ...fieldMeta, ...fieldOption, validate: validateRules, }; this.fieldsStore.setFieldMeta(name, meta); // 保存field到store中 if (fieldMetaProp) { inputProps[fieldMetaProp] = meta; } if (fieldDataProp) { inputProps[fieldDataProp] = this.fieldsStore.getField(name); } return inputProps; }, ...getCacheBind
getCacheBind方法,缓存函数,使用bind方法绑定上下文并缓存部分参数,返回一个新的函数,用做onChange及数据校验。
... getCacheBind(name, action, fn) { if (!this.cachedBind[name]) { this.cachedBind[name] = {}; } const cache = this.cachedBind[name]; if (!cache[action]) { cache[action] = fn.bind(this, name, action); // 绑定参数并返回 } return cache[action]; }, ...onCollectCommon
在getFieldProps方法中看到利用getCacheBind方法当无rules的时候绑定了一个onCollect方法,onCollect方法主要调用onCollectCommon方法,并将得到的结果保存到store。
onCollectCommon(name, action, args) { const fieldMeta = this.fieldsStore.getFieldMeta(name); if (fieldMeta[action]) { // 如果getFieldDecorator方法中的参数定义了onChange,则触发改onChange fieldMeta[action](...args); } else if (fieldMeta.originalProps && fieldMeta.originalProps[action]) { // 如果输入组件绑定了onChange,则触发该onChange fieldMeta.originalProps[action](...args); } const value = fieldMeta.getValueFromEvent ? // 获取更新后的value,兼容原生组件e.target.value fieldMeta.getValueFromEvent(...args) : getValueFromEvent(...args); if (onValuesChange && value !== this.fieldsStore.getFieldValue(name)) { // 如果Form.create时用户定义有onValuesChange,则触发 const valuesAll = this.fieldsStore.getAllValues(); const valuesAllSet = {}; valuesAll[name] = value; Object.keys(valuesAll).forEach(key => set(valuesAllSet, key, valuesAll[key])); onValuesChange(this.props, set({}, name, value), valuesAllSet); } const field = this.fieldsStore.getField(name); // 获取合并field,并返回 return ({ name, field: { ...field, value, touched: true }, fieldMeta }); },onCollectValidate
在有输入rules的时候getCacheBind方法绑定onCollectValidate作为onChange事件,该方法做了除了调用了onCollectCommon事件以外,还调用了校验方法validateFieldsInternal。
validateFieldsInternal该方法主要是从store中获取rules校验规则并标准化后,使用async-validator模块进行校验,并把结果保存到store中,本文不做讲解。
setFields该方法主要是设置store中的field,因为store的数据是不可观测的数据,不会引起页面的重渲染,该方法也负责调用forceUpdate()强制更新页面。
setFields(maybeNestedFields, callback) { const fields = this.fieldsStore.flattenRegisteredFields(maybeNestedFields); // 处理field嵌套问题 this.fieldsStore.setFields(fields); if (onFieldsChange) { // 如果设置有FieldsChange事件监听事件变化,则触发事件 const changedFields = Object.keys(fields) .reduce((acc, name) => set(acc, name, this.fieldsStore.getField(name)), {}); onFieldsChange(this.props, changedFields, this.fieldsStore.getNestedAllFields()); } this.forceUpdate(callback); // 强制更新视图 },最后
主要方法大概就上面这些,其中流程差不多在每次setFields之前,会在store中存一个field的变化字段fieldMeta,在最后强制更新页面的时候,将该变量取出来做处理后覆盖到field,所有数据保存在field中,并提供了一些hock方法如setFieldsValue、validateFields等方法设置和获取store中的field字段和值。
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/94364.html
我们今天来讲讲关于ahooks 源码,我们目标主要有以下几点: 深入了解 React hooks。 明白如何抽象自定义 hooks,且可以构建属于自己的 React hooks 工具库。 小建议:培养阅读学习源码的习惯,工具库是一个对源码阅读不错的选择。 列表页常见元素 后台管理系统中常见典型列表页包括筛选表单项、Table表格、Pagination分页这三部分。 针对使用 Ant...
摘要:是校验表单组件数据正确性的字段,其值为数组,里面的数组元素可以为。所以数组元素如果为的话,其内容就是的。到目前为止,表单适合大部分的表单应用场景。 JSON表单 描述 JSON表单是一个基于React的抽象组件,它可以把JSON数据格式描述的表单转换成项目中的表单,它可以用简短的几行代码,快速的生成Form表单。JSON表单的优点是: 可以快速构建出一个表单 表单的数据、逻辑、视图分...
摘要:结构项目结构如下,负责外层封装,负责事件绑定与渲染控制。节点通过决定事件绑定情况,即通过属性绑定事件情况,事件控制组件的属性,这里就不详细说了。为隐藏状态下的添加的,并包裹懒加载组件。 引言 antd的Tooltip组件在react-componment/trigger的基础上进行封装,而组件Popover和Popconfirm是使用Tooltip组件的进行pop,在react-com...
摘要:在装载组件之前调用会组件的构造函数。当实现子类的构造函数时,应该在任何其他语句之前调用设置初始状态绑定键盘回车事件,添加新任务修改状态值,每次修改以后,自动调用方法,再次渲染组件。可以通过直接安装到项目中,使用或进行引用。 首先我们看一下我们完成后的最终形态:TodoMvc: showImg(https://segmentfault.com/img/remote/14600000085...
摘要:前言这是一个基于实现的一个简单登入例子,对于刚上手想进一步了解,前端页面如何请求到服务层路由处理数据库操作返回结果到页面这整个过程的同学比较有用。我们来看下登入请求处理。操作演示演示用户名不存在,密码错误及成功登入。 前言 这是一个基于node实现的一个简单登入例子,对于刚上手node想进一步了解,前端页面如何请求到服务层 -> 路由处理 -> 数据库操作 -> 返回结果到页面这整个过...
阅读 2112·2021-11-16 11:45
阅读 1182·2021-10-22 09:53
阅读 4002·2021-09-07 10:26
阅读 1209·2021-09-06 15:00
阅读 2072·2019-08-28 18:09
阅读 2795·2019-08-26 14:06
阅读 3933·2019-08-26 13:48
阅读 1295·2019-08-26 12:11