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

React中如何从同构子组件表单创建对象数组

实现重复子表单收集结构化联系人数组的React方案

看起来你想要通过重复渲染的子表单组件,收集数据并整合成指定格式的联系人对象数组。我来帮你优化现有代码,让它更符合React的数据流模式,同时完美满足你的需求:

先梳理下现有代码的小问题

  • 子组件里给表单字段的name加了formNo后缀(比如salutation1),这会导致最终收集的对象字段名不符合你想要的格式(你需要的是salutation而不是salutation1)
  • 父组件目前只是单个打印子组件传递的数据,没有把两个联系人的数据整合到一个数组里
  • 用循环直接赋值useRef的方式不太安全,React不推荐在循环里直接操作ref

优化后的完整实现

1. 父组件:统一管理联系人状态数组

父组件用useState初始化一个包含两个空对象的数组,用来存储两个联系人的数据。给每个子组件传递索引和更新方法,让子组件的输入变化直接同步到父组件的状态中:

import React, { useState } from 'react';
import { Grid, FormLabel, Button } from '@mui/material';
import DeliveryContact from './DeliveryContact';

const ParentComponent = () => {
  // 初始化两个空联系人对象,对应两个子表单
  const [contacts, setContacts] = useState([{}, {}]);

  // 更新对应索引的联系人数据
  const updateContact = (index, updatedFields) => {
    setContacts(prevContacts => {
      const newContacts = [...prevContacts];
      // 合并已有数据和新输入的字段
      newContacts[index] = { ...newContacts[index], ...updatedFields };
      return newContacts;
    });
  };

  // 提交时可以直接使用contacts数组,就是你想要的格式
  const handleSubmit = () => {
    console.log("最终联系人数组:", contacts);
    // 这里可以把contacts传递给后端或者做其他处理
  };

  return (
    <React.Fragment>
      {contacts.map((_, index) => (
        <React.Fragment key={index}>
          <Grid item xs={12}>
            <FormLabel component="legend">
              Delivery Contact Person : ({index + 1})
            </FormLabel>
          </Grid>
          <DeliveryContact
            formNo={index + 1}
            contactData={contacts[index]}
            onUpdate={(fields) => updateContact(index, fields)}
          />
        </React.Fragment>
      ))}
      <Grid item xs={12} style={{ marginTop: 20 }}>
        <Button variant="contained" onClick={handleSubmit}>
          提交联系人信息
        </Button>
      </Grid>
    </React.Fragment>
  );
};

export default ParentComponent;

2. 子组件:作为受控组件传递输入变化

子组件不再维护独立状态(或保留本地状态做临时存储),接收父组件的contactData作为当前值,输入变化时通过onUpdate把字段传递给父组件。同时去掉不必要的name后缀,保证字段名和期望的对象结构一致:

import React, { useState, useEffect } from 'react';
import { Grid, TextField, MenuItem } from '@mui/material';

// 定义默认的联系人字段结构
const defaultContact = {
  salutation: '',
  name: '',
  phone: '',
  email: '',
  designation: ''
};

const DeliveryContact = ({ formNo, contactData, onUpdate }) => {
  // 子组件本地临时状态,也可以直接用contactData做纯受控组件
  const [values, setValues] = useState(defaultContact);

  // 同步父组件传递的最新数据到本地
  useEffect(() => {
    setValues(prev => ({ ...prev, ...contactData }));
  }, [contactData]);

  const handleInputChange = ({ target }) => {
    const { name, value } = target;
    const updatedValues = { ...values, [name]: value };
    setValues(updatedValues);
    // 实时把更新的字段传给父组件
    onUpdate({ [name]: value });
  };

  return (
    <React.Fragment>
      <Grid item xs={12} sm={6}>
        <TextField
          select
          fullWidth
          margin="dense"
          id="salutation"
          name="salutation" // 保持原始字段名,不加后缀
          onChange={handleInputChange}
          value={values.salutation}
          variant="outlined"
          label="Salutation"
        >
          <MenuItem value="Mr.">Mr.</MenuItem>
          <MenuItem value="Ms.">Ms.</MenuItem>
        </TextField>
      </Grid>
      <Grid item xs={12} sm={6}>
        <TextField
          id="deliveryContactName"
          name="name" // 对应期望对象的name字段
          label="Delivery Contact Name"
          fullWidth
          variant="outlined"
          margin="dense"
          onChange={handleInputChange}
          value={values.name}
          inputProps={{ maxLength: 40 }}
        />
      </Grid>
      {/* 补充剩余需要的字段 */}
      <Grid item xs={12} sm={6}>
        <TextField
          name="phone"
          label="Phone Number"
          fullWidth
          variant="outlined"
          margin="dense"
          onChange={handleInputChange}
          value={values.phone}
          type="number"
        />
      </Grid>
      <Grid item xs={12} sm={6}>
        <TextField
          name="email"
          label="Email"
          fullWidth
          variant="outlined"
          margin="dense"
          onChange={handleInputChange}
          value={values.email}
          type="email"
        />
      </Grid>
      <Grid item xs={12} sm={12}>
        <TextField
          name="designation"
          label="Designation"
          fullWidth
          variant="outlined"
          margin="dense"
          onChange={handleInputChange}
          value={values.designation}
        />
      </Grid>
    </React.Fragment>
  );
};

export default DeliveryContact;

关键改进说明

  • 统一状态管理:父组件的contacts数组就是最终你需要的格式,随时可以使用,避免了子组件状态分散难以整合的问题
  • 去掉字段后缀:保证收集的字段名和你期望的name、salutation等一致,不会出现name1这种多余的后缀
  • 替代ref调用:用回调函数传递变化,比useImperativeHandle更符合React的单向数据流模式,代码更易维护
  • 清晰的索引对应:每个子组件对应数组中的一个元素,不会出现数据混乱的情况

最终效果

当用户在两个子表单中输入数据后,父组件的contacts状态就会变成你想要的格式:

[
  {
    "name": "asdasdas123da",
    "phone": 9898989898,
    "email": "test@mailinator.com",
    "designation": "Some Manager",
    "salutation": "Mr."
  },
  {
    "name": "asdasdas",
    "phone": 9898989898,
    "email": "test@mailinator.com",
    "designation": "Some Manager",
    "salutation": "Mr."
  }
]

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 07:03:24