Drcus | 王亚振

Drcus | 王亚振

随便写,记录点东西

React 组件之 FormItems 封装

发布于:  

简单说一下技术背景, 主要是 Ant Design 的版本是 3.x 的版本,React 是什么版本不紧要。

用法示例:

getCardFields = (card) => {
  const fields = [
      {
        id: `topic-${card.id}`,
        label: '话题',
        rules: [{
          transform: (value) => {
            const isValid = value && value.label && value.value;
            if (!isValid) {
              return '';
            }
            return value.value;
          },
          required: true,
          message: '请输入话题',
        }],
        initialValue: card[`topic-${card.id}`],
        type: TopicAutoComplete,
        props: {
          onChange: () => {
            this.props.form.setFieldsValue({
              [`article-${card.id}`]: {label: '', value: ''},
            });
          }
        }
      },
      {
        id: `article-${card.id}`,
        label: '文章',
        rules: [{
          transform: (value) => {
            const isValid = value && value.label && value.value;
            if (!isValid) {
              return '';
            }
            return value.value;
          },
          required: true,
          message: '请输入文章',
        }],
        initialValue: card[`article-${card.id}`],
        type: ArticleAutoComplete,
        props: {
          subjectId: card[`topic-${card.id}`] && card[`topic-${card.id}`].value,
        },
      },
      {
        id: `description-${card.id}`,
        label: '介绍',
        required: true,
        initialValue: card[`description-${card.id}`],
        type: 'Input',
      },
  ];
  return fields
}



// 渲染

renderFormCard = () => {
	const { cards } = this.state;
	const formItemLayout = {
	    labelCol: {
	      xs: { span: 24 },
	      sm: { span: 6 },
	    },
	    wrapperCol: {
	      xs: { span: 24 },
	      sm: { span: 12 },
	    },
	  };
	return cards.map((item, index) => {
	  return (
	    <div className="card-form" key={item.id}>
	      <i className="card-index">{index + 1}</i>
	      <FormItems formItemLayout={formItemLayout} fields={this.getCardFields(item)} form={this.props.form} />
	    </div>
	  )
	})
};

组件结构

class FormItems extends React.Component {

	// 解析 props
	parseProps = (props, field) => {...}

	// 解析处理 如 select, group 等情况
	parseChildren = (type, children) => {...}

	parseRules = (field, rules) => {...}

	renderFields() {...}

	render() {
	    const { className } = this.props;
	    return (
	      <div className={className}>
	        {this.renderFields()}
	      </div>
	    )
	}
}

下面是详细的每个方法的实现

parseProps = (props, field) => {
    if (!props) {
      return {}
    }

    const fooProps = {}
    for (const key in props) {      
      fooProps[key] = props[key]
    }

    fooProps.children = this.parseChildren(field.type, fooProps.children)
    return fooProps
}

parseChildren = (type, children) => {
    let childrenElement = null
    switch (type) {
      case 'RadioGroup':
        childrenElement = children.map(item => (
          <Radio key={item.value} value={item.value}>{item.label}</Radio>
        ))
        break
      case 'CheckboxGroup':
        childrenElement = children.map(item => (
          <Checkbox key={item.value} value={item.value}>{item.label}</Checkbox>
        ))
        break
      case 'InputTextArea':
        childrenElement = children.map(item => (
          <Input.TextArea key={item.value} value={item.value}>{item.label}</Input.TextArea>
        ))
        break
      case 'Select':
        childrenElement = children.map(item => (
          <Option key={item.value} value={item.value}>{item.label}</Option>
        ))
        break
      // no default
    }
    return childrenElement
}

// 只是便捷处理了必填
parseRules = (field, rules) => {
    rules = rules || []
    if (field.required) {
      return [
        {
          required: true, message: `请输入${field.label}`,
        },        
        ...rules,
      ]
    }
    return rules
}

主要渲染函数

renderFields() {
    const { fields } = this.props
    let { formItemLayout } = this.props

    return fields.map((field, index) => {
      let node
      const { getFieldDecorator } = this.props.form
      let valuePropName = 'value'
      let { initialValue, props, dynamicProps, rules, extraNode } = field
      const { type, label, id } = field

      if (type instanceof Object) {
        node = React.createElement(type);
      } else {
        switch (type) {
          case 'Input':
            node = (<Input />)
            break
          case 'Input.TextArea':
            node = (<Input.TextArea />)
            break
          case 'display':
            node = (<Input disabled />)
            break

          case 'Select':
            node = (<Select />)
            break
          
          case 'RadioGroup':
            node = (<RadioGroup />)
            break

          case 'CheckboxGroup':
            node = (<CheckboxGroup />)
            break

          case 'Checkbox':
            valuePropName = 'checked'
            node = (<Checkbox />)
            break
          case 'DatePicker':
            node = <DatePicker />
            break
          case 'RangePicker':
            node = (<RangePicker />)
            break

          default:
            node = (<Input />)
        }
      }

      props = this.parseProps(props, field, fields, index)
      rules = this.parseRules(field, rules)
      const { children, ...otherProps } = props

      const moreProps = {};
      for (let p in dynamicProps) {
        if (dynamicProps.hasOwnProperty(p) && typeof dynamicProps[p] === 'function') {
          moreProps[p] = dynamicProps[p].call(this, field, fields, index);
        }
      }

      node = React.cloneElement(node, { ...otherProps, ...moreProps }, children)

      formItemLayout = formItemLayout || {
        labelCol: {
          xs: { span: 24 },
          sm: { span: 8 },
        },
        wrapperCol: {
          xs: { span: 24 },
          sm: { span: 9 },
        },
      }

      if (typeof field.showIf === 'function' && !field.showIf.call(this, field, fields, index)) {
        return null
      }

      return (
        <FormItem
          className={classnames({ [`${field.className}`]: !!field.className })}
          {...formItemLayout}
          label={label}
          key={id}
        >
          {getFieldDecorator(id, {
            initialValue,
            valuePropName,
            rules,
          })(
            node,
          )}
          {
            extraNode
          }
        </FormItem>
      )
    })
}

这样的 form 封装才是比较好的,可以借鉴学习一下。

~^_^~ 一片小花园 ?

赏赐