需求:现有一个form表单,但是其中一个元素比较复杂,并不是简单的输入框或者下拉框之类的.但是我又希望能通过form.validateFields().then()去获得它的值,就不需要在当前页面写大量相关的逻辑了.
方案:将其封装成自动绑定值的组件
(1)form.item会给组件的props注册两个关键属性,
value:value是form.item对应的name属性的值,可用作默认值以及返显,
onChange函数.:用于监听value元素值的变化,并将其传给form.item使其可以通过相关api获得其值.
(2)注意value值不一定和页面渲染的数据相同.比如:我需要同时渲染三个input,那我页面渲染的时候需要一个数组,但是可能传给后端的时候是用","这类符合分开的.所以onCange()传递的值最好分开处理.
如:
<Form.Item name='name'> <InputList ></InputList > <Form.Item>
子组件InputList内部代码(简易版):
(这段代码是临时在博客中编写的,主要是展示value和onChange的功能,不确保能正确执行)
const InputList = (props)=>{ const { value ,onChange } =props //value 是form表单中"name"对应的字段值 const getValue = ()=>{ let newList = JSON.parse(JSON.stringify(value )) //深拷贝一波,防止对原数据造成影响 let value = newList .join(',') onChange(value) } useEffect((item,index)=>{ getValue () //传递值的时机可以自己把握,可以是点击事件也可以是其他事件 },[value.length]) return <> {value ?.length>0 && value.map((item,index)=>{ return <div key = {index}>{item}</div> //如果组件内部有增删等功能,可以不建议用index,可以在渲染之前给每个iten绑定一个特定的key,不然会出现删除之后出现相同key,然后dom元素不重新渲染的问题 })} </> }
现在结合实际情景说明一下子组件内部代码:
如图为一个可以增删改的input列表
实际代码:
(这段代码有很大的优化空间,因为刚开始没考虑清楚,后续补补填填导致代码逻辑越来越复杂,不过主要是展示自定义form.item元素的实现,具体业务逻辑可以省略)
/* *先解释一下下面会出现的容易混淆的几个变量分别代表什么 *dataList:用于渲染页面,增删的时候改变,输入框内容变更的时候不变,因为改变它会导致页面重新渲染,输入框失去焦点,每次就只能输入一个字符然后又得重新获得焦点 *datas:实际的已经改变的,用于下次页面更新的数据保留.所以实际上跟页面看上去是一样的值 *dataLists:下次实际更新的值,这里是根据datas和dataList来判断的,绝大部分情况是跟datas一致的 *dataValue :onChange()传递的值,因为业务需求,做了非空去除,页面实际上可以有空输入框,但是提交的时候不能有空值 * */ let datas = ['']; let disable = false; let value = ''; let dataValue = undefined; let inputIndex = 0; class InputList extends React.Component { formRef = React.createRef(); state = { dataList: [{ data: '', indexe: 0, default: true }], value: [], }; static getDerivedStateFromProps(nextProps, prevState) { // Should be a controlled component. if ('value' in nextProps) { let { value } = nextProps; if (_.isEqual(value, prevState.origin)) { return null; } if (!_.isArray(value)) { if (!value) value = []; else value = [value]; } //因为这个生命周期会监听每次变化,所以必须严格控制其条件 if ( value && prevState?.dataList[0]?.default ) { datas = cloneDeep(value); dataValue = cloneDeep(value); let file = []; file = value.map((v, i) => { return { data: v, index: i }; }); // } return { dataList: file, }; } else { return null; } } return null; } componentDidMount() { const { disabled, value } = this.props; disable = disabled; } triggerChange = (files, index) => { const onChange = this.props.onChange; if (datas === undefined) { datas = []; } if (dataValue === undefined) { dataValue = []; } datas[index] = files.target.value; dataValue[index] = files.target.value; value = files.target.value; inputIndex = index; if (onChange) { dataValue = dataValue.filter((item) => item !== null && item !== ''); if (dataValue.length === 0) { dataValue = undefined; } onChange(dataValue); } }; addInput = () => { const { dataList } = this.state; let arr = dataList; arr.push({ data: '', index: dataList.length, }); this.setState({ dataList: arr, }); if (datas === undefined) { datas = []; } if (dataValue === undefined) { dataValue = []; } datas.push(''); dataValue.push(''); }; delIputn = (index) => { let { dataList } = this.state; let arr = []; let dataLists = []; if (datas === undefined) { datas = []; //因为有undefined的情况,所以预先处理 } if (dataValue === undefined) { dataValue = []; //因为有undefined的情况,所以预先处理 } dataValue.splice(index, 1); datas.splice(index, 1); dataLists = datas?.length > 0 ? datas.map((item, index) => (item = { data: item, index: item.index })) : dataList; dataLists?.length > 0 && dataLists.forEach((item, index1) => { arr.push(item); }); this.setState({ dataList: dataLists, }); const onChange = this.props.onChange; // let datas = cloneDeep(dataList); if (onChange) { dataValue = dataValue.filter((item) => item !== null && item !== ''); if (dataValue.length === 0) { dataValue = undefined; //设置成undefined是为了让校验不通过 } onChange(dataValue); } }; render() { const { dataList } = this.state; const { disabled } = this.props; return ( <> <div> {dataList.map((item, index) => { return ( <div style={{ display: 'flex', marginBottom: '10px' }}> <Input onChange={(value) => this.triggerChange(value, index)} // value={item.data} key={index + item.data + item.index} disabled={disabled} defaultValue={item.data} ></Input> {(index > 0 || dataList?.length > 1) && !disabled ? ( <div className={styles.delBox} onClick={() => this.delIputn(index)} > 删除 </div> ) : ( <div className={styles.delBox}></div> )} </div> ); })} </div> {!disabled ? ( <div className={styles.addBox} onClick={() => this.addInput()}> 添加管制名单 </div> ) : null} </> ); } } export default InputList;