There is a requirement in development to validate each entry in the cell and provide operations for adding and deleting rows.

Since the Form component can easily validate data, we thought of using the From component and the Table component to implement this requirement. Can achieve all the function points in the requirements, but the scheme is not perfect, friends can refer to 👉 link here.

The renderings are here

import React from "react";
import ReactDOM from "react-dom";
import PropTypes from "prop-types";
import "antd/dist/antd.css";
import { Form, Input, Table, Select, AutoComplete, Icon } from "antd";
import "./map.css";

class SetMapping extends React.Component {
 static propTypes = {
   form: PropTypes.shape({}).isRequired,
 };

 state = {
   mappingState: [],
   appnameList: [],
   tagList: []
 };

 componentDidMountSyslogIPValidator = () => ({rule, value, callback) => {syslogIPValidator: (rule, value, callback) => {if(value && value.trim() ! = ="") {const reg = / ^ (\ d {1, 3} | \ *) \. (\ d {1, 3} | \ *) \. (\ d {1, 3} | \ *) \. (\ d {1, 3} | \ *) $/ I;if(! reg.test(value.trim())) { callback("IP address format error");
       } else if (this.checkIfExistedIP(value)) {
         callback("Duplicate IP address");
       } else{ callback(); }}}}); checkIfExistedIP = value => { const { form } = this.props; const { getFieldValue } = form; const mapping = getFieldValue("mapping") | | []; const mappingArray = mapping ? mapping.filter(one => one.ip === value) : [];if (mappingArray.length > 1) {
     return true;
   }
   return false; }; // Add a line of data addOneMapping = () => {const {form} = this.props; const { getFieldsValue, validateFields } = form; const { mappingState } = this.state; const mapping = getFieldsValue().mapping || mappingState; // If there is any data in the form --> check if the previous one is completeif(mapping && mapping.length ! == 0) { validateFields(["mapping"], { force: true }, errors => {
       if(! errors) { mapping.push({ ip:"",
           appname: "",
           tag: "",
           charset: ""}); this.setState({ mappingState: mapping }); }}); }else {
     mapping.push({
       ip: "",
       appname: "",
       tag: "",
       charset: ""}); this.setState({ mappingState: mapping }); }}; // mappingState is the data source used to render the table. RemoveOneMapping => {const {form} = this.props; const { getFieldValue } = form; const mapping = getFieldValue("mapping");
   mapping.splice(index, 1);

   this.setState({
     mappingState: mapping
   });
   form.setFieldsValue({
     mapping
   });
 };

 render() {
   const { mappingState } = this.state;
   const { form, appnameList, tagList } = this.props;
   const { getFieldDecorator } = form;

   const PLACEHOLDER = "Please enter";

   const tableTitle = [
     {
       title: "ip",
       dataIndex: "ip",
       key: "ip",
       width: 205,
       render: (ip, one, index) => (
         <Form.Item>
           {getFieldDecorator(`mapping[${index}].ip`, {
             initialValue: ip || "",
             validateFirst: true,
             validateTrigger: ["onChange"."onFocus"],
             rules: [
               {
                 required: true,
                 message: "Please enter IP address"
               },
               this.syslogIPValidator()
             ]
           })(
             <Input
               size="small"
               style={{ width: "170px" }}
               autoComplete="off"
               placeholder={PLACEHOLDER}
             />
           )}
         </Form.Item>
       )
     },
     {
       title: "appname",
       dataIndex: "appname",
       key: "appname",
       width: 205,
       render: (appname, one, index) => (
         <Form.Item>
           {getFieldDecorator(`mapping[${index}].appname`, {
             initialValue: appname || "",
             validateFirst: true,
             validateTrigger: ["onChange"."onBlur"],
             rules: [
               {
                 required: true,
                 message: "Please enter appName"
               }
             ]
           })(
             <AutoComplete
               size="small"
               dataSource={appnameList}
               style={{ width: "170px"}} placeholder={PLACEHOLDER} filterOption={(inputValue, option) => option.props.children .toUpperCase() .indexOf(inputValue.toUpperCase()) ! == -1 } /> )} </Form.Item> ) }, { title:"tag",
       dataIndex: "tag",
       key: "tag",
       width: 205,
       render: (tag, one, index) => (
         <Form.Item>
           {getFieldDecorator(`mapping[${index}].tag`, {
             initialValue: tag || "",
             validateFirst: true,
             validateTrigger: ["onChange"."onBlur"],
             rules: [
               {
                 required: true,
                 message: "Please enter tag"
               }
             ]
           })(
             <AutoComplete
               size="small"
               dataSource={tagList}
               style={{ width: "170px"}} placeholder={PLACEHOLDER} filterOption={(inputValue, option) => option.props.children .toUpperCase() .indexOf(inputValue.toUpperCase()) ! == -1 } /> )} </Form.Item> ) }, { title:"charset",
       dataIndex: "charset",
       key: "charset",
       width: 110,
       render: (charset, one, index) => (
         <Form.Item>
           {getFieldDecorator(`mapping[${index}].charset`, {
             initialValue: charset || "utf-8"
           })(
             <Select size="small">
               <Select.Option value="utf-8" style={{ fontSize: 12 }}>
                 utf-8
               </Select.Option>
               <Select.Option value="gbk" style={{ fontSize: 12 }}>
                 gbk
               </Select.Option>
             </Select>
           )}
         </Form.Item>
       )
     },
     {
       title: "Operation",
       key: "operation",
       width: 60,
       render: (operation, one, index) => (
         <Icon
           style={{ height: "42px" }}
           type="delete"
           onClick={() => this.removeOneMapping(index)}
         />
       )
     }
   ];

   return (
     <div className="setMapping">
       <Table
         bordered
         size="small"
         className="tableBody"
         rowKey={(record, index) => index}
         columns={tableTitle}
         dataSource={mappingState}
         pagination={false}
       />
       <span className="addNew"OnClick ={this.addonemapping}> Add a new mapping </span> </div>); } } const SetMappingForm = Form.create({ name:"register" })(SetMapping);

ReactDOM.render(<SetMappingForm />, document.getElementById("root"));

Copy the code

AntDesign also has a similar implementation, you can check it out, 👉 link here. This question is also discussed on Github, and other partners’ methods are linked here at 👉