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
Causes and consequences 1. When using the ansible...
Table of contents Preface 1. Download MySQL from ...
one. Overview of IE8 Compatibility View <br /&...
Operating environment: MAC Docker version: Docker...
If you are using the latest Ubuntu Server version...
MySQL database reports ERROR 1045 (28000): Access...
1 What is MVCC The full name of MVCC is: Multiver...
To do a paginated query: 1. For MySQL, it is not ...
Calculation of the box model <br />Margin + ...
Background: position: sticky is also called stick...
mysql 5.6.35 winx64 free installation version con...
Preface Many years ago, I was a newbie on the ser...
Table of contents Preface 1. Install Docker 2. In...
The specific steps of installing mysql5.7.18 unde...
Table of contents 1. Date 2. RegExp 3. Original p...