I encountered a pitfall when writing dynamic forms before. Using index subscript as key will cause bugs, and they are very serious! I have time today to write down an article to record: how to deal with and logic I'm using the antd3 version, the forms for 3 and 4 are a little different, but the difference shouldn't be that big. need: 1. Select the type to switch and display the fixed template 2. By adding new fields, you can dynamically increase or decrease each row in the form 3. Control whether the fields in each row need to be filled in 4. Backfill parameters when editing Effect picture: Some key codes: import React, { Component } from 'react'; import styles from './index.less'; import { Table, Button, Select, Popconfirm, Modal, Form, Input, Radio, Row, Col, Tooltip, Icon, message, Pagination, InputNumber, } from 'antd'; const Option = Select.Option; const FormItem = Form.Item; let id = 0; @Form.create() class Index extends Component { marketId = 0; state = { selectType: '', orderType: 1, //Article 1 Map 2 typeLoading: false, isEdit: false, lookVisible: false, visible: false, pageSize: 10, pageNum: 1, keyWord: '', row: {}, typeList: {}, mock: {}, mapType: [{ 'fieldName': 'name', 'isImg': 0, 'order': 0, 'remarks': 'Name', }, { 'fieldName': 'label', 'isImg': 0, 'order': 0, 'remarks': 'Labels', }, { 'fieldName': 'lon', 'isImg': 0, 'order': 0, 'remarks': 'Longitude', }, { 'fieldName': 'lat', 'isImg': 0, 'order': 0, 'remarks': 'Latitude', }], articleType: [{ 'fieldName': 'name', 'isImg': 0, 'order': 0, 'remarks': 'Name', }, { 'fieldName': 'label', 'isImg': 0, 'order': 0, 'remarks': 'Labels', }], }; /** * Generate the required data format for the dynamic table value * @param values * @returns {[]} */ createValues = (values) => { const { row } = this.state; const data = []; const newValues = { // Use a new object to carry the submitted data...values, }; const fieldNameData = []; // Save fieldName value const remarksData = []; // Save remarks value const isImgData = []; // Save isImg value const orderData = []; // Save orderData value const fieldName = RegExp(/fieldName/); const remarks = RegExp(/remarks/); const isImg = RegExp(/isImg/); for (const key in newValues) { if (fieldName.test(key)) { fieldNameData.push(newValues[key]); } } for (const key in newValues) { if (remarks.test(key)) { remarksData.push(newValues[key]); } } for (const key in newValues) { if (isImg.test(key)) { isImgData.push(newValues[key]); } } for (const key in newValues) { if (isImg.test(key)) { orderData.push(newValues[key]); } } fieldNameData.forEach((item, index) => { data.push({ fieldName: item, remarks: remarksData[index], isImg: isImgData[index], order: orderData[index], id: row.dataType ? row.dataType.id : '', }); }); return data; }; handleOk = e => { this.props.form.validateFields((err, values) => { if (!err) { const { row, isEdit } = this.state; const params = { dataType: { name: values.name, type: values.type, id: row.dataType ? row.dataType.id : '', }, typeFields: [], }; params.typeFields = this.createValues(values); if (isEdit) { editType(params).then(res => { if (res.code === 0) { message.info('Modification successful'); this.setState({ visible: false, isEdit: false, }); this.fetchTypeList(); this.props.form.resetFields(); } }); } else { addType(params).then(res => { if (res.code === 0) { message.info('Added successfully'); this.setState({ visible: false, isEdit: false, }); this.fetchTypeList(); this.props.form.resetFields(); } }); } } }); }; lookOrEditTypeModal = (flag, record) => { const { articleType, mapType } = this.state; if (flag === 'add') { //Add the default article template this.marketId = articleType.length + 1; //Set the dynamic key tag length this.setState({ visible: true, row: { typeFields: articleType }, }); } else if (flag === 'edit') { this.setState({ visible: true, }); getType({ dataTypeId: record.id }).then(res => { if (res.code === 0) { this.marketId = res.data.typeFields.length + 1; //Set the dynamic key tag length this.setState({ row: res.data, isEdit: flag === 'edit', }); } }); } else { this.setState({ lookVisible: true, }); getType({ dataTypeId: record.id }).then(res => { if (res.code === 0) { this.setState({ row: res.data, }); } }); } }; onChangeType = (value) => { const { form } = this.props; const { orderType, row, articleType, mapType } = this.state; this.props.form.resetFields(); const params = {}; if (value === 1) { //Article type params['typeFields'] = articleType; this.marketId = articleType.length + 1; } else { params['typeFields'] = mapType; this.marketId = mapType.length + 1; } this.setState({ row: params, orderType: value, }); }; //Delete method! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! removeFile = k => { const { form } = this.props; const keys = form.getFieldValue('keys'); if (keys.length === 1) { return; } form.setFieldsValue({ keys: keys.filter(key => key !== k), }); }; //Add method! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! addFile = () => { const { form } = this.props; const keys = form.getFieldValue('keys'); const nextKeys = keys.concat(this.marketId++); form.setFieldsValue({ keys: nextKeys, }); }; judgeIsTemplet = (data) => { if (!data) { return false; } if ((data.fieldName === 'lat') || (data.fieldName === 'lon') || (data.fieldName === 'label') || (data.fieldName === 'name')) { return true; } }; handleValidator = (rule, val, callback) => { if (!val) { callback(); } let validateResult = /^[5A-Za-z0-9-\_]+$/.test(val); if (!validateResult) { callback('Please enter the correct table field'); } callback(); }; columns = [ { title: 'Type name', dataIndex: 'name', key: 'name', width: 500, }, { title: 'Type', dataIndex: 'type', key: 'type', render: (text) => { return text === 1 ? 'Article' : 'Map'; }, }, { title: 'Operation', dataIndex: 'address', key: 'address', render: (text, record) => { return <div> <Button type='link' onClick={() => this.lookOrEditTypeModal('look', record)}>View</Button> <Button type='link' onClick={() => this.lookOrEditTypeModal('edit', record)}>Edit</Button> <Popconfirm title="Confirm deletion?" onConfirm={() => this.deleteTypeClick(record)}> <Button type='link'>Delete</Button> </Popconfirm> </div>; }, }, ]; render() { const { selectType, typeLoading, mock, row, isEdit, typeList, keyWord, lookVisible } = this.state; const { getFieldDecorator, getFieldValue } = this.props.form; let typeFields = row.typeFields || []; const initData = []; typeFields.forEach((item, index) => {//Set the default keys array according to the real data initData.push(index); }); getFieldDecorator('keys', { initialValue: initData }); //Add the keys field to the form and set the default value. This will generate the effect of editing and backfilling when editing. const keys = getFieldValue('keys'); const formItems = keys.map((k) => ( <Row gutter={12} key={k} className={styles.form_row}> <FormItem label="field" key={`fieldName_${k}`}> {getFieldDecorator(`fieldName_${k}`, { initialValue: row.typeFields[k] ? row.typeFields[k].fieldName : '', validateTrigger: ['onChange', 'onBlur'], //Check the timing of child node values rules: [{ required: true, message: 'Please enter the English field!', }, { validator: this.handleValidator, }], })(<Input placeholder="Please enter the English field" max={30} disabled={this.judgeIsTemplet(row.typeFields[k])}/>)} </FormItem> <FormItem label="Name" key={`remarks_${k}`}> {getFieldDecorator(`remarks_${k}`, { initialValue: row.typeFields[k] ? row.typeFields[k].remarks : '', validateTrigger: ['onChange', 'onBlur'], rules: [{ required: true, message: 'Please enter a Chinese name!', }], })(<Input placeholder="Please enter the Chinese name" disabled={this.judgeIsTemplet(row.typeFields[k])}/>)} </FormItem> <FormItem label="Sort" key={`order_${k}`}> {getFieldDecorator(`order_${k}`, { initialValue: row.typeFields[k] ? row.typeFields[k].order : 0, })(<InputNumber style={{width:75}} placeholder="Sort" />)} </FormItem> <FormItem label="Picture" key={k}> {getFieldDecorator(`isImg_${k}`, { initialValue: row.typeFields[k] ? row.typeFields[k].isImg : 0, rules: [{ required: true, }], })(<Radio.Group disabled={this.judgeIsTemplet(row.typeFields[k])}> <Radio value={0}>No</Radio> <Radio value={1}>Yes</Radio> </Radio.Group>)} </FormItem> {!this.judgeIsTemplet(row.typeFields[k]) ? ( <Icon type="minus-circle" onClick={() => this.removeFile(k)} title='Delete'/> ) : null} </Row> )); return ( <div className={styles.wrap_type}> <Modal title="Type Management" visible = {this.state.visible} onOk={this.handleOk} onCancel = {this.handleCancel} width={890} // className={styles.modal_type} maskClosable={false} > <Form layout='inline'> <Row style={{ textAlign: 'center', marginBottom: 14 }}> <FormItem label="Select Type"> {getFieldDecorator('type', { initialValue: row.dataType ? row.dataType.type : 1, rules: [{ required: true, }], })(<Select onChange={this.onChangeType} disabled={isEdit} style={{ width: 200 }}> <Option value={1}>Article type</Option> <Option value={2}>Map type</Option> <Option value={3} disabled={true}>File type</Option> </Select>)} </FormItem> <FormItem label="Type name"> {getFieldDecorator('name', { initialValue: row.dataType ? row.dataType.name : '', rules: [{ required: true, message: 'Please enter the type name!', }], })(<Input placeholder="Please enter the type name" style={{ width: 200 }}/>)} </FormItem> </Row> {formItems} <div style={{ margin: 'auto', textAlign: 'center' }}> <Button icon="plus" onClick={this.addFile} style={{ marginTop: 10 }}>Add new field</Button> </div> </Form> </Modal> </div> ); } } export default Index; The key point is to set a marketID as the dynamically added key, and then use its value as the dynamic key. (Never use the array subscript index as the key)! This is the end of this article about how to dynamically increase or decrease forms with react antd. For more information about how to dynamically increase or decrease forms with react antd, please search 123WORDPRESS.COM's previous articles or continue to browse the following related articles. I hope you will support 123WORDPRESS.COM in the future! You may also be interested in:
|
<<: How to call the interrupted system in Linux
>>: MySQL DeadLock troubleshooting full process record
I won’t talk about the use of SSL certificates. F...
Original Intention The reason for making this too...
This article will discuss these 4 principles as t...
Table of contents Defining the HTML structure Inp...
MariaDB is installed by default in CentOS, which ...
1. Environment: MySQL-5.0.41-win32 Windows XP Pro...
1. Environment: CentOS7, Openssl1.1.1k. 2. Concep...
1. Add users and groups 1. Add mysql user group #...
js date time format Convert the date and time to ...
MySql always pops up a MySQLInstallerConsole.exe ...
Table of contents 1. Don’t treat objects as Maps ...
This seems to be no longer possible with the new ...
Table of contents 1. MySQL join buffer 2. JoinBuf...
I recently encountered a strange thing when debug...
Python connects to MySQL to modify and query data...