You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React动态增删行 删除指定行时下方所有行被连带删除问题

React动态增删表单行异常修复

我开发了一款支持动态添加、删除组件行的React程序,具体实现与问题如下:

行组件(Dselect)

行组件样式如下:
行组件样式

行组件实现代码:

import Select from "react-select";
import { Row, Col, Button, Container, Form } from "react-bootstrap";
import React, { useState, useEffect } from "react";

const baseurl = "http://192.168.121.10:5000/api";
const Dselect = (props) => {
  const [pdtCodeOptions, setPdtCodeOptions] = useState([]);
  const [catListOptions, setCatListOptions] = useState([]);
  const [pdtListOptions, setPdtListOptions] = useState([]);

  const [selectedCat, setSelectedCategory] = useState();
  const [selectedPcode, setSelectedPCode] = useState();
  const [selectedPName, setSelectedPName] = useState();

  useEffect(() => {
    const fetchCat = async () => {
      const res = await fetch(`${baseurl}/cat`);
      const cList = await res.json();

      let catOpt = [];
      for (let i = 0; i < cList.length; i++) {
        catOpt.push({ label: cList[i].category, value: cList[i].category });
      }
      setCatListOptions(catOpt);
    };

    const fetchPdtCode = async () => {
      const res = await fetch(`${baseurl}/pdtlist`);
      const pList = await res.json();
      const pdtList = [];
      for (let i = 0; i < pList.length; i++) {
        pdtList.push({ label: pList[i].pdt_id, value: pList[i].pdt_id });
      }
      setPdtCodeOptions(pdtList);
    };

    fetchCat();
    fetchPdtCode();
  }, []);

  const getPdtDetails = async (e) => {
    const pdtID = e.value;

    const res = await fetch(`${baseurl}/details`, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ data: pdtID }),
    });
    const detList = await res.json();

    const pdt_name = detList[0].pdt_name;
    const category = detList[0].category;

    setSelectedCategory({ label: category, value: category });
    setSelectedPName({ label: pdt_name, value: pdt_name });
  };

  const updateList = async (e) => {
    const catVal = e.value;

    const res = await fetch(`${baseurl}/pdtList`, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ data: catVal }),
    });
    const pdtList = await res.json();

    const catPdtList = [];
    for (let i = 0; i < pdtList.length; i++) {
      catPdtList.push({
        label: pdtList[i].pdt_name,
        value: pdtList[i].pdt_name,
      });
    }
    setPdtListOptions(catPdtList);
  };

  const getPdtcode = async (e) => {
    const pdtVal = e.value;

    const res = await fetch(`${baseurl}/pdtCode`, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ data: pdtVal }),
    });
    const code = await res.json();
    setSelectedPCode(code);
  };

  return (
    <Row className="mt-3">
      <Col lg={3} md={3} xs={3}>
        <Select
          options={pdtCodeOptions}
          placeholder="Select product"
          value={selectedPcode}
          onChange={(e) => {
            setSelectedPCode(e);
            getPdtDetails(e);
          }}
        />
      </Col>

      <Col lg={3} md={3} xs={3}>
        <Select
          options={catListOptions}
          placeholder="Category"
          value={selectedCat}
          onChange={(e) => {
            setSelectedCategory(e);
            updateList(e);
          }}
        />
      </Col>
      <Col lg={3} md={3} xs={3}>
        <Select
          options={pdtListOptions}
          onChange={(e) => {
            setSelectedPName(e);
            getPdtcode(e);
          }}
          value={selectedPName}
          placeholder="Select product"
        />
      </Col>
      <Col lg={2} md={2} xs={2}>
        <Button
          type="button"
          onClick={() => props.onDelete(props.r)}
          className="btn btn-secondary"
        >
          Del
        </Button>
      </Col>
    </Row>
  );
};

export default Dselect;

父组件增删逻辑

点击Add按钮即可新增组件行,修复后的父组件代码如下:

import React from "react";
import Dselect from "../components/dSelect";
import { Form, Button } from "react-bootstrap";
import { useState } from "react";

const TableShow = () => {
  // 替换原直接存储JSX的逻辑,改为存储行唯一标识
  const [rowIds, setRowIds] = useState([0]);

  const addRow = () => {
    const newId = rowIds.length > 0 ? Math.max(...rowIds) + 1 : 0;
    setRowIds([...rowIds, newId]);
  };

  const delRow = (targetIndex) => {
    // 替换原splice删2个元素的错误逻辑,用不可变数据方式过滤
    setRowIds(rowIds.filter((_, index) => index !== targetIndex));
  };

  return (
    <div>
      <Button onClick={addRow}>Add</Button>
      <Form>
        {rowIds.map((id, index) => (
          <Dselect key={id} r={index} onDelete={delRow} />
        ))}
      </Form>
    </div>
  );
};

export default TableShow;

核心错误点

  • 初始渲染的第一行组件没有传入onDelete回调,点击删除按钮时触发参数异常
  • 原delRow方法中splice第二个参数错误传入2,每次删除操作会移除从索引位置开始的2个元素
  • 原逻辑直接将JSX元素存在state中管理,自定义key和数组实际索引不匹配,React虚拟DOM diff时出现渲染错位,导致点击删除后下方行全部消失
  • 子组件三个Select的onChange回调中,更新状态时错误传入旧state值,选值功能本身存在失效bug

问题复现

删除前存在多行
➡
删除后下方行全部消失


内容的提问来源于stack exchange,提问作者Maya

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 16:33:15