利用ant-design封装react的地址输入组件

时间:2020-12-13 19:27:12

  在上一节利用element-ui封装地址输入的组件留下了个尾巴,说react搭配ant-design封装一下地址输入的组件的。本来应该早早就完成的,但是由于这中间发生了一些事情,导致了突发性的换了工作,所以一直耽误到现在,今天就把这个尾巴结束吧!

  事实上,ant-design在form组件内提供了自定义表单控件的写法,这里需要做的也就是把这个自定义表单控件搬过来而已。

  利用ant-design封装react的地址输入组件

  其实,关键在于,属性值value,和事件onChange。然后,组件内部在constructor的时候,转换传递过来的value,当Cascader的onChange事件发生时,获取新的数据值,调用this.props.onChange事件,同理,在Input组件发生onChange事件时,也是获取新的e.target.value,调用this.props.onChange事件。

  到这里,基本上这个组件封装就完成了。

  对比一下element-ui发现,两者本质上是一致的。

  都是利用内部组件各自onChange时,获取最新的值,然后调用props.onChange事件,把数值传递给使用者。很容易理解,在Cascader发生onChange时,调用props.onChange,比较不好理解的是,Input组件也是在onChange时,调用props.onChange,而不是我们通常理解的发生input时,调用props.onChange,这是为什么呢?

  不管是element-ui,还是ant-desigin,都没有向我们暴露Input组件的input事件,所以我们就没有办法获取Input组件的input事件,所以只好退而求其次的使用change事件。然后,在vue中,为了使用v-model指令,我们需要添加mode:{event: 'change', prop:'value' }属性,而在react中,没有类似的指令,都是以属性形式传递的,所以直接使用value和onChange即可。

  还是直接贴代码吧!

  

import React, { Component } from 'react'
import { Form, Input, Row, Col, Cascader } from 'antd'
// 城市选择过滤函数
function filter (inputValue, path) {
  return path.some(option => option.name.toLowerCase().indexOf(inputValue.toLowerCase()) > -1)
}

class AddressInput extends Component {
  static getDerivedStateFromProps (nextProps) {

    // Should be a controlled component.
    if ('value' in nextProps) {
      return {
        ...(nextProps.value || {}),
      }
    }
    return null
  }
  constructor(props) {
    super(props)

    const value = props.value || {}
    this.state = {
      values: value.values || [],
      registeredAddress: value.registeredAddress || '',
    }
    this.cascader = {}
  }

  handleAddressChange = e => {
    const registeredAddress = e.target.value || ''
    if (!('value' in this.props)) {
      this.setState({ registeredAddress })
    }
    this.triggerChange({ registeredAddress })
  }

  handleCascaderChange = (values, items) => {
    this.setState({ values })
    this.cascader = {
      areaCode: values.slice(-1)[0],
      areaProvince: items[0].name,
      areaCity: items[1].name,
      areaDistrict: items.slice(-1)[0].name
    }
    this.triggerChange({ values })
  }

  triggerChange = changedValue => {
    const onChange = this.props.onChange
    if (onChange) {
      const item = Object.assign({}, this.state, this.cascader, changedValue)
      if (item.values) delete item.values
      onChange(item)
    }
  }

  render () {
    const { values, registeredAddress } = this.state
    // console.log(values)
    const { allowClear = false, regionData = [], size = 'default' } = this.props
    return (
      <Row gutter={8} type="flex" justify="space-between" >
        <Col span={12}>
          <Cascader
            allowClear={allowClear}
            size={size}
            value={values}
            options={regionData}
            fieldNames={{ value: 'code', label: 'name' }}
            showSearch={{ filter }}
            onChange={this.handleCascaderChange}
            placeholder="请选择省市区" />
        </Col>
        <Col span={12}>
          <Input
            size={size}
            placeholder="请输入具体地址"
            value={registeredAddress}
            onChange={this.handleAddressChange}
          />
        </Col>
      </Row>
    )
  }
}

export default Form.create({ name: 'customized_form_controls' })(AddressInput)