react+antd form表单项动态添加数据的实现
需求效果功能如图:
代码:
1 | const [relateList, setRelateList] = useState(getPath(initInfo, 'relateLinkList' , [] || [])); |
这个是因为此页面还实现了编辑功能,需要渲染初始数据,此时需要监听一个已有的列表,进行删除时候的删除处理,否则初始化后删除会出问题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 | <Form.Item label= "关联链接" vvspan={24} labelCol={{ md: 3 }} wrapperCol={{ md: 21 }} style={{ marginBottom: '0' }} > <span style={{ color: urlList.length < 20 ? '#FFA22D' : 'rgba(0, 0, 0, 0.25)' , cursor: 'pointer' }} onClick={add} > 添加链接 </span> </Form.Item> <Col vvspan={24} offset={3} className={styles.relateLinkList}> {urlList.map(k => ( <Form.Item vvspan={24} labelCol={{ md: 3 }} wrapperCol={{ md: 21 }}> <div key={k} style={{ position: 'relative' }}> {getFieldDecorator(`relateLinkList[${k}]`, { validateTrigger: [ 'onBlur' ], rules: [ { min: 1, max: 2000, message: '不可超过2000字' }, { pattern: /^(ht|f)tps?:\/\/(([a-zA-Z0-9_-])+(\.)?)*(:\d+)?(\/((\.)?(\?)?=?&?([a-zA-Z0-9_-]| #|%)(\?)?)*)*$/i, message: '请输入有效网址!' } ], })( <Input style={{ width: '100%' , paddingRight: '30px' , boxSizing: 'border-box' }} /> )} <i className= "icon iconfont iconshanchu" style={{ position: 'absolute' , right: '12px' , top: '0' , cursor: 'pointer' , color: '#333333' }} onClick={() => remove(k)} /> </div> </Form.Item> ))} </Col> |
initialValue: getPath(relateList, `${k - 1}`, '')
备注:这里遍历重新添加form.item的原因是加了网址校验,如果只遍历getFieldDecorator,校验只校验第一条数据
方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | const remove = k => { setUrlList(urlList.filter(key => key !== k)); setRelateList(relateList.filter((_, index) => index !== k - 1)); }; const add = () => { const keys = form.getFieldValue( 'relateLinkList' ); if (urlList.length < 20) { if ((keys && keys[keys.length - 1]) || keys === undefined) { const nextKeys = urlList.concat(urlList.length + 1); setUrlList(nextKeys); } } else { message.error( '上限20个' ); } }; |
数据提交的校验:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | const newLinkList = values.relateLinkList && values.relateLinkList.filter(item => !!item); const params = { ...values, id: 0, relateLinkList: newLinkList, }; const result = await launchTask(params); if (result.code === 10000) { message.success(result.msg); router.goBack(); pageTabUtil.closeTab( '/work/task/addTask' ); } else { message.error(result.msg); } |
-------------------------------------接上文警告线-----------------------------------------------上面的实现,发生了一个意想不到的问题:如果有初始值,那么增删都会出现bug----------------------------
因此重新封装了组件:
👇表单项代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | <Form.Item label= "关联链接" vvspan={24} labelCol={{ md: 3 }} wrapperCol={{ md: 21 }} style={{ marginBottom: '0' }} > {getFieldDecorator( 'relateLinkList' , { validateTrigger: [ 'onChange' , 'onBlur' ], rules: [ { validator(_, value, callback) { value.some(item => { const errorMessage = linkRef.current.getValidateError(item.link); if (errorMessage) { callback(errorMessage); return true ; } return false ; }); callback(); } } ], initialValue: relativeLinkData })(<RelativeLink childRef={linkRef} />)} </Form.Item> |
拿到的数据初始化:
1 | const relativeLinkData = (initInfo.relateLinkList || []).map((item, index) => ({ key: index + 1, link: item })); |
封装的组件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 | import React from 'react' ; import { message, Input } from 'antd' ; import cn from 'classnames' ; import styles from './RelativeLink.less' ; let id; const validators = [ { validator: link => link && link.length <= 2000, message: '不可超过2000字' }, { validator: link => /^(ht|f)tps?:\/\/(([a-zA-Z0-9_-])+(\.)?)*(:\d+)?(\/((\.)?(\?)?=?&?([a-zA-Z0-9_-]| #|%|\)|\()(\?)?)*)*$/i.test( link ), message: '请输入有效网址' } ]; const getValidateError = link => { let msg; if (!link) { return msg; } validators.some(item => { if (!item.validator(link)) { msg = item.message; return true ; } return false ; }); return msg; }; const RelativeLink = ({ value, onChange, childRef }) => { const handleAdd = () => { if (value && value.length < 20) { if (!id) id = value.length || 0; id += 1; onChange([...value, { key: id }]); } else { message.error( '上限20个' ); } }; const handleOnChange = (e, key) => { onChange( value.map(it => { if (it.key === key) { return { ...it, link: e.target.value }; } return it; }) ); }; const handleDelete = key => { onChange(value.filter(it => it.key !== key)); }; React.useImperativeHandle(childRef, () => ({ getValidateError })); return ( <> <span style={{ color: value.length < 20 ? '#FFA22D' : 'rgba(0, 0, 0, 0.25)' , cursor: 'pointer' }} onClick={handleAdd} > 添加链接 </span> {value.map(item => ( <div key={item.key} style={{ position: 'relative' }} className={cn({ [styles.success]: !getValidateError(item.link) })} > <Input style={{ width: '100%' , paddingRight: '30px' , boxSizing: 'border-box' }} value={item.link} onChange={e => handleOnChange(e, item.key)} /> <i className= "icon iconfont iconshanchu" style={{ position: 'absolute' , right: '12px' , top: '0' , cursor: 'pointer' , color: '#333333' }} onClick={() => handleDelete(item.key)} /> </div> ))} </> ); }; export default RelativeLink; |
引入页面:
1 | import { RelativeLink } from './components' ; |
1 | const linkRef = React.useRef(); |
提交时候的数据处理为网址数组:
1 2 3 | const newLinkList = values.relateLinkList && values.relateLinkList.filter(item => item.link && item.link.length > 0).map(item => item.link); |
实际效果【当添加后没输入则不校验,否则必须小于2000字且要求网址格式正确】
拿到的初始数据:
渲染效果:
随意增删,也不会出现渲染问题
分类:
react从入门到放弃
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
· [.NET]调用本地 Deepseek 模型
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析