antd4 源码学习 :表单

首先。vue 的数据流是双向的,而 react 的数据流是单向的。

SpringBoot2.x入门:使用MyBatis

这意味着什么? 这意味着,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);   (
关于createElement方式…)   参数 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 ,
它没有也不需要自力的数据管理,表单容器的数据转变会直接反映到表单。 function getFieldMeta(name) {       this.fieldsMeta[name] = this.fieldsMeta[name] || {};       return this.fieldsMeta[name];     }

 

原创文章,作者:7h28新闻网,如若转载,请注明出处:https://www.7h28.com/archives/23573.html