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

React动态表单组件增删异常及值获取问题求助

问题解决:动态属性组件的删除异常与表单值获取

核心问题

你当前的实现存在两个关键问题:

  • 将React组件实例直接存入attributes状态,导致删除时依赖的position与数组索引绑定,数组变化后索引重排,误删后续组件
  • 无法追踪属性组件的输入值,因为组件实例未与状态绑定

修复方案

1. 重构状态结构

把attributes从存储组件改为存储属性数据对象,每个对象带唯一ID(避免索引冲突),示例结构:

{ id: '唯一标识', name: '', quantity: '' }

2. 修改添加/删除逻辑

  • 添加属性时生成带唯一ID的空数据对象,而非组件实例
  • 删除属性时通过唯一ID过滤数组,不再依赖索引或初始position

3. 受控组件绑定输入

让ProductAttribute成为受控组件,将输入值同步到父组件状态,确保所有输入值可被追踪

修改后的完整代码

import { useState } from "react";
import { Link } from "react-router-dom";
import { SlArrowLeft } from "react-icons/sl";
import { IoMdClose } from "react-icons/io";

// 生成唯一ID的简易方法,也可使用uuid库
const generateId = () => {
  return `attr-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
};

const CreateProduct = () => {
  const [productName, setProductName] = useState("");
  const [productPrice, setProductPrice] = useState("");
  const [attributes, setAttributes] = useState([]);

  const addAttribute = () => {
    setAttributes([
      ...attributes,
      { id: generateId(), name: "", quantity: "" }
    ]);
  };

  const deleteAttribute = (id) => {
    setAttributes(attributes.filter(attr => attr.id !== id));
  };

  const updateAttribute = (id, field, value) => {
    setAttributes(attributes.map(attr => {
      if (attr.id === id) {
        return { ...attr, [field]: value };
      }
      return attr;
    }));
  };

  const handleSubmit = (e) => {
    e.preventDefault();
    // 获取所有表单数据,包含属性
    const productData = {
      name: productName,
      price: productPrice,
      attributes: attributes
    };
    console.log("提交的数据:", productData);
    // 此处可编写提交到数据库的逻辑
  };

  return (
    <div className="max-w-screen-xl mx-auto px-2 py-3">
      <h1 className="text-xl font-bold leading-tight tracking-tight text-gray-900 py-2 md:text-2xl flex items-center">
        <Link to="/gestionar-productos" className="mr-4">
          <SlArrowLeft />
        </Link>
        Crear nuevo producto
      </h1>

      <form onSubmit={handleSubmit}>
        <div className="grid gap-4 sm:grid-cols-2 sm:gap-6">
          <div className="w-full">
            <label
              htmlFor="name"
              className="block mb-2 text-sm font-medium text-gray-900"
            >
              Nombre del producto
            </label>
            <input
              type="text"
              name="name"
              id="name"
              className="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-primary-600 focus:border-primary-600 block w-full p-2.5"
              placeholder="Escribe el nombre del producto"
              required=""
              value={productName}
              onChange={(e) => setProductName(e.target.value)}
            />
          </div>
          <div className="w-full">
            <label
              htmlFor="price"
              className="block mb-2 text-sm font-medium text-gray-900"
            >
              Precio
            </label>
            <input
              type="number"
              name="price"
              id="price"
              className="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-primary-600 focus:border-primary-600 block w-full p-2.5"
              placeholder="$1000"
              required=""
              value={productPrice}
              onChange={(e) => setProductPrice(e.target.value)}
            />
          </div>
          {attributes.map((attribute) => (
            <ProductAttribute
              key={attribute.id}
              attribute={attribute}
              deleteAttribute={deleteAttribute}
              updateAttribute={updateAttribute}
            />
          ))}
        </div>
        <button
          className="inline-flex items-center px-5 py-2.5 mt-4 sm:mt-6 text-sm font-medium text-center text-white bg-primary-700 rounded-lg focus:ring-4 focus:ring-primary-200 dark:focus:ring-primary-900 hover:bg-primary-800"
          type="button"
          onClick={addAttribute}
        >
          Añadir atributo al producto
        </button>
        <button
          type="submit"
          className="w-full text-white bg-red-400 hover:bg-red-500 focus:ring-4 focus:outline-none focus:ring-primary-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center mt-6"
        >
          Crear producto
        </button>
      </form>
    </div>
  );
};

const ProductAttribute = ({ attribute, deleteAttribute, updateAttribute }) => {
  return (
    <div className="py-5 px-5 border rounded-lg relative">
      <button
        onClick={() => deleteAttribute(attribute.id)}
        className="py-2 absolute top-1 right-4"
      >
        <IoMdClose size={20} color="red" />
      </button>
      <div className="w-full">
        <label
          className="block mb-2 text-sm font-medium text-gray-900"
        >
          Atributo del producto
        </label>
        <input
          type="text"
          className="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-primary-600 focus:border-primary-600 block w-full p-2.5"
          placeholder="Escribe el atributo del producto"
          required=""
          value={attribute.name}
          onChange={(e) => updateAttribute(attribute.id, "name", e.target.value)}
        />
      </div>
      <div className="w-full mt-2">
        <label
          className="block mb-2 text-sm font-medium text-gray-900"
        >
          Cantidad
        </label>
        <input
          type="number"
          className="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-primary-600 focus:border-primary-600 block w-full p-2.5"
          placeholder="10"
          required="Escribe la cantidad del atributo"
          value={attribute.quantity}
          onChange={(e) => updateAttribute(attribute.id, "quantity", e.target.value)}
        />
      </div>
    </div>
  );
};

export default CreateProduct;

关键改进点

  • 状态存储数据而非组件,避免React渲染异常
  • 用唯一ID标识每个属性,删除逻辑精准无误
  • 所有输入框绑定到状态,提交时直接获取完整产品数据
  • 父组件统一管理状态,子组件仅负责渲染和触发更新

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 15:02:09