React Form组件的实现封装杂谈(2)

表单的字段一般包括两部分,一部分是标题,另一部分是内容。ZentForm通过getControlGroup这一高阶函数对结构和样式做了一些封装,它的入参是要显示的组件:

export default Control => {
  render() {
    return (
      <div className={groupClassName}>
        <label className="zent-form__control-label">
          {required ? <em className="zent-form__required">*</em> : null}
          {label}
        </label>
        <div className="zent-form__controls">
          <Control {...props} {...controlRef} />
          {showError && (
            <p className="zent-form__error-desc">{props.error}</p>
          )}
          {notice && <p className="zent-form__notice-desc">{notice}</p>}
          {helpDesc && <p className="zent-form__help-desc">{helpDesc}</p>}
        </div>
      </div>
     );                          
  }
}

这里用到的label和error等信息,是通过Field组件传入的:

<Field
  label="预约门店:"
  name="dept"
  component={CustomizedComp}
  validations={{
    required: true,
  }}
  validationErrors={{
    required: '预约门店不能为空',
  }}
  required
/>

这里的CustomizedComp是通过getControlGroup封装后返回的组件。

字段与表单之间的交互是一个需要考虑的问题,表单需要知道它包含的字段值,需要在适当的时机对字段进行校验。ZentForm的实现方式是在Form的高阶组件内维护一个字段数组,数组内容是Field的实例。后续通过操作这些实例的方法来达到取值和校验的目的。

ZentForm的使用方式如下:

class FieldForm extends React.Component {
  render() {
    return (
      <Form>
        <Field
          name="name"
          component={CustomizedComp}
      </Form>
    )
  }
}

export default createForm()(FieldForm);

其中Form和Field是组件库提供的组件,CustomizedComp是自定义的组件,createForm是组件库提供的高阶函数。在createForm返回的组件中,维护了一个fields的数组,同时提供了attachToForm和detachFromForm两个方法,来操作这个数组。这两个方法保存在context对象当中,Field就能在加载和卸载的时候调用了。简化后的代码如下:

/**
 * createForm高阶函数
 */
const createForm = (config = {}) => {
  ...
  return WrappedForm => {
    return class Form extends Component {
      constructor(props) {
        super(props);
        this.fields = [];
      }
      
      getChildContext() {
        return {
          zentForm: {
            attachToForm: this.attachToForm,
            detachFromForm: this.detachFromForm,
          }
        }
      }
      
      attachToForm = field => {
        if (this.fields.indexOf(field) < 0) {
          this.fields.push(field);
        }
      };
    
      detachFromForm = field => {
        const fieldPos = this.fields.indexOf(field);
        if (fieldPos >= 0) {
          this.fields.splice(fieldPos, 1);
        }
      };
      
      render() {
        return createElement(WrappedForm, {...});
      }
    } 
  }
}

/**
 * Field组件
 */
class Field extends Component {
  componentWillMount() {
    this.context.zentForm.attachToForm(this);
  }
  
  componentWillUnmount() {
    this.context.zentForm.detachFromForm(this);
  }
  
  render() {
    const { component } = this.props;
    return createElement(component, {...});
  }
}
      

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:http://www.heiqu.com/231.html