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
FireFox is a commonly used browser with many exte...
VMware version: VMware-workstation-full-16 VMware...
Pure js implements a single-click editable table ...
Table of contents 1. Introduction to the Implemen...
1. Change the transparency to achieve the gradual...
Originally, this seventh chapter should be a deep ...
This article uses an example to describe how MySQ...
Recently, when I was working on a conference heal...
The command pattern is a behavioral design patter...
I believe that many friends who are new to web pag...
The installation process of VMwarea will not be d...
I recently wrote a mobile page at work, which was...
This article mainly introduces the implementation...
1 Implementation Principle This is done using the...
Table of contents 1. prototype (explicit prototyp...