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
相关产品推荐
相关产品推荐

