在上一节利用element-ui封装地址输入的组件留下了个尾巴,说react搭配ant-design封装一下地址输入的组件的。本来应该早早就完成的,但是由于这中间发生了一些事情,导致了突发性的换了工作,所以一直耽误到现在,今天就把这个尾巴结束吧!
事实上,ant-design在form组件内提供了自定义表单控件的写法,这里需要做的也就是把这个自定义表单控件搬过来而已。
其实,关键在于,属性值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)