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

React:如何向map渲染的组件传递函数props并获取输入值数组

问题根因

你现在获取不到动态行输入值的核心原因有两个:

  1. 状态分层隔离:Dselect的输入值存在组件自身的state里,TableShow自己维护行列表,两层都没有把数据往最外层的FormSubmit传,表单组件自然拿不到值
  2. 没有做回调透传:你没有给map渲染的行组件传递「值更新时回传数据」的函数prop,子组件不知道要把数据往哪送

实现方案

核心思路是状态上提:把所有动态行的增删逻辑、行数据统一放到最外层的表单组件管理,中间的表格组件只做透传,行组件只负责渲染和触发回调,不要自己存最终要提交的输入数据。

具体实现步骤

1. 改造最外层FormSubmit组件

把原来放在TableShow里的行列表、增删行方法、行数据更新方法都挪到这里,统一维护所有表单数据:

import React, { useState } from "react";
import { Container, Button, Form, Row, Col } from "react-bootstrap";
import TableShow from "./tablePage";

const baseurl = "http://localhost:5000/api";

const FormSubmit = () => {
  const [inputs, setInputs] = useState({
    instruct_date: "",
    user_name: "",
  });  
  // 统一维护动态行数据,每条数据带唯一id,和行一一对应
  const [rowList, setRowList] = useState([{ 
    id: 0, 
    values: { pdt_code: "", pdt_category: "", pdt_name: "" } 
  }]);
  const [idCount, setIdCount] = useState(0);

  // 普通输入框change逻辑
  const changeHandler = (event) => {
    const name = event.target.name;
    const value = event.target.value;
    setInputs((values) => ({ ...values, [name]: value }));
  };

  // 新增行
  const addRow = () => {
    const newId = idCount + 1;
    setIdCount(newId);
    setRowList([...rowList, { id: newId, values: { pdt_code: "", pdt_category: "", pdt_name: "" } }]);
  };

  // 删除行
  const delRow = (rowId) => {
    setRowList(rowList.filter(row => row.id !== rowId));
  };

  // 接收子组件回传的单行更新值,这个就是要传给map渲染组件的函数类型prop
  const updateRowValue = (rowId, fieldName, fieldValue) => {
    setRowList(rowList.map(row => {
      if (row.id === rowId) {
        return {
          ...row,
          values: { ...row.values, [fieldName]: fieldValue }
        }
      }
      return row;
    }))
  };

  // 提交逻辑
  const SubmitHandler = async (event) => {
    event.preventDefault();
    // 直接从rowList提取所有行的输入值,就是你要的rowInput数组
    const rowInput = rowList.map(row => row.values);
    await fetch(`${baseurl}/submit/instruct`, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({
        input1: inputs,
        input2: rowInput,
      }),
    });
  };

  return (
    <Container>
      <h4> FORM </h4>
      <Form>
        <Row>
          <Col l={4} md={4} xs={6}>
            <div className="form-group form-inline">
              <label>Date</label>
              <input
                type="date"
                className="form-control"
                name="instruct_date"
                value={inputs.instruct_date}
                onChange={changeHandler}
              />
            </div>
          </Col>
          <Col l={4} md={4} xs={6}>
            <div className="form-group form-inline">
              <label>Name</label>
              <input
                type="text"
                className="form-control"
                name="team_name"
                value={inputs.user_name}
                onChange={changeHandler}
              />
            </div>
          </Col>
        </Row>
        {/* 把所有方法和行数据传给表格组件 */}
        <TableShow 
          rowList={rowList}
          addRow={addRow}
          delRow={delRow}
          updateRowValue={updateRowValue}
        />
        <Button className="my-3" onClick={SubmitHandler}>
          Submit
        </Button>
      </Form>
    </Container>
  );
};

export default FormSubmit;

2. 改造TableShow组件

去掉自身的状态,只做props透传,把接收到的回调函数继续传给每个map渲染的Dselect组件:

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

const TableShow = (props) => {
  const { rowList, addRow, delRow, updateRowValue } = props;
  return (
    <div>
        <Button className="mt-3 btn-sm btn-info" onClick={addRow}>
          Add rows
        </Button>
        {/* map渲染时把回调函数作为prop传给每个行组件,带上行id做区分 */}
        {rowList.map((row) => (
          <Dselect 
            key={row.id} 
            r={row.id}
            rowValues={row.values}
            onDelete={delRow}
            onValueChange={updateRowValue}
          />
        ))}
    </div>
  );
};

export default TableShow;

这里传函数prop没有特殊写法,和传普通prop完全一致,不管渲染多少个子组件,每个子组件触发回调时只要带上自己的唯一id,父组件就能精准更新对应行的数据。

3. 改造Dselect行组件

去掉自身存输入值的inputs状态,选择器值变化时调用父组件传进来的onValueChange回调回传数据,同时修复你原来onChange里把旧值设回去的bug:

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

const baseurl = "http://localhost:5000/api";

const Dselect = (props) => {
  const { r, rowValues, onDelete, onValueChange } = props;
  const [pdtCodeOptions, setPdtCodeOptions] = useState([]);
  const [catListOptions, setCatListOptions] = useState([]);
  const [pdtListOptions, setPdtListOptions] = useState([]);

  // 选中值从父组件传进来的rowValues取,不再自己维护
  const selectedCat = rowValues.pdt_category ? { label: rowValues.pdt_category, value: rowValues.pdt_category } : null;
  const selectedPcode = rowValues.pdt_code ? { label: rowValues.pdt_code, value: rowValues.pdt_code } : null;
  const selectedPName = rowValues.pdt_name ? { label: rowValues.pdt_name, value: rowValues.pdt_name } : null;

  useEffect(() => {
    const fetchCat = async () => {
      const res = await fetch(`${baseurl}/cat`);
      const cList = await res.json();
      let catOpt = cList.map(item => ({ label: item.category, value: item.category }));
      setCatListOptions(catOpt);
    };

    const fetchPdtCode = async () => {
      const res = await fetch(`${baseurl}/pdtlist`);
      const pList = await res.json();
      const pdtList = pList.map(item => ({ label: item.pdt_id, value: item.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;
    // 联动选择的值直接回传给父组件
    onValueChange(r, "pdt_category", category);
    onValueChange(r, "pdt_name", 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 = pdtList.map(item => ({ label: item.pdt_name, value: item.pdt_name }));
    setPdtListOptions(catPdtList);
  };

  const getPdtcode = async (e) => {
    const pdtName = e.value;
    const category = rowValues.pdt_category;
    const data = { category: category, pdtName: pdtName };
    const res = await fetch(`${baseurl}/pdtCode`, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify(data),
    });
    const code = await res.json();
    // 回传获取到的产品编码
    onValueChange(r, "pdt_code", code);
  };

  const changeHandler = (e, opt) => {
    const name = opt.name;
    const value = e.value;
    // 选中值变化时直接调用回调,传给父组件:行id、字段名、字段值
    onValueChange(r, name, value);
  };

  return (
    <Row className="mt-3">
      {r + 1}
      <Col lg={3} md={3} xs={3}>
        <Select
          name="pdt_code"
          options={pdtCodeOptions}
          placeholder="Select product Code"
          value={selectedPcode}
          onChange={(e, opt) => {
            changeHandler(e, opt);
            getPdtDetails(e);
          }}
        />
      </Col>

      <Col lg={3} md={3} xs={3}>
        <Select
          options={catListOptions}
          name="pdt_category"
          placeholder="Category"
          value={selectedCat}
          onChange={(e, opt) => {
            changeHandler(e, opt);
            updateList(e);
          }}
        />
      </Col>

      <Col lg={3} md={3} xs={3}>
        <Select
          name="pdt_name"
          options={pdtListOptions}
          onChange={(e, opt) => {
            changeHandler(e, opt);
            getPdtcode(e);
          }}
          value={selectedPName}
          placeholder="Select product Name"
        />
      </Col>

      <Col lg={2} md={2} xs={2}>
        <Button
          type="button"
          onClick={() => onDelete(r)}
          className="btn btn-secondary"
        >
          Del
        </Button>
      </Col>
    </Row>
  );
};

export default Dselect;

效果说明

改完之后,不管你动态加多少行、删多少行,每个选择器的值变化都会实时同步到最外层FormSubmit的rowList状态里,提交的时候直接提取所有行的values就能得到你要的输入值数组,不会再出现拿不到值的情况。
另外你原来删除行时整段消失的问题也会一起解决,因为现在每一行的key是唯一不变的id,React不会错误复用组件状态。

内容的提问来源于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 17:25:04