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

如何通过Modal输入框更新React Table表格行数据

React Table 行数据更新实现方案

需求:点击React Table行内按钮打开Modal,在Modal输入框填写数据后点击保存,关闭Modal并更新对应行的指定列数据。
流程:React Table → 点击行按钮 → 打开Modal并传入当前行数据 → 填写数据点击保存 → 关闭Modal,父组件接收新数据 → 更新对应行数据

实现步骤

1. 父组件状态管理

在包含Table和Modal的父组件中,维护三个核心状态:

  • 表格数据源(tableData)
  • Modal的显示状态(isModalOpen)
  • 当前选中的行数据(selectedRow)

2. 实现Modal组件

创建受控Modal组件,接收选中行数据、关闭回调、保存回调作为props,内部维护输入框状态,点击保存时将新数据传递给父组件。

3. 修改Table的点击事件

在Table的行按钮点击时,设置选中行数据并打开Modal。

4. 处理保存逻辑

父组件接收Modal传递的新数据后,更新表格数据源中对应行的内容。

完整代码示例

父组件(包含Table和Modal)

import { useState } from 'react';
import MyTable from './myTable';
import EditModal from './EditModal';

const ParentComponent = () => {
  // 初始表格数据(可替换为接口请求结果)
  const [tableData, setTableData] = useState([
    { tx_id: 'tx001', incident_id: 'inc001', resolvedstatus: 'pending', resolved_comments: '' },
    { tx_id: 'tx002', incident_id: 'inc002', resolvedstatus: 'resolved', resolved_comments: '已处理' },
  ]);
  const [isModalOpen, setIsModalOpen] = useState(false);
  const [selectedRow, setSelectedRow] = useState<null | typeof tableData[0]>(null);

  // 打开Modal并传入选中行数据
  const viewActionModalOpen = (rowData: typeof tableData[0]) => {
    setSelectedRow(rowData);
    setIsModalOpen(true);
  };

  // 关闭Modal并清空选中行
  const closeModal = () => {
    setIsModalOpen(false);
    setSelectedRow(null);
  };

  // 更新指定行数据
  const updateRowData = (newData: { resolvedstatus: string; resolved_comments: string }) => {
    if (!selectedRow) return;
    // 通过唯一标识tx_id定位行,创建新数组避免直接修改原数据
    const updatedData = tableData.map(row => 
      row.tx_id === selectedRow.tx_id 
        ? { ...row, ...newData } 
        : row
    );
    setTableData(updatedData);
    closeModal();
  };

  return (
    <div>
      <MyTable 
        tableData={tableData} 
        viewActionModalOpen={viewActionModalOpen} 
      />
      <EditModal
        isOpen={isModalOpen}
        selectedRow={selectedRow}
        onClose={closeModal}
        onSave={updateRowData}
      />
    </div>
  );
};

export default ParentComponent;

myTable.tsx(修改后)

import { useTable, useSortBy, useFilters } from 'react-table';
import { Fragment } from 'react';

interface MyTableProps {
  tableData: Array<{
    tx_id: string;
    incident_id: string;
    resolvedstatus: string;
    resolved_comments: string;
  }>;
  viewActionModalOpen: (row: any) => void;
}

const MyTable = ({ tableData, viewActionModalOpen }: MyTableProps) => {
  const COLUMNS = [
    {
      Header: 'Transaction ID',
      accessor: 'tx_id',
      disableFilters: true,
    },
    {
      Header: 'Incident ID',
      accessor: 'incident_id',
      disableFilters: true,
    },
    {
      Header: 'Resolved Status',
      accessor: 'resolvedstatus',
      disableFilters: true,
    },
    {
      Header: 'Resolved Comments',
      accessor: 'resolved_comments',
      disableFilters: true,
    },    
  ];

  const {
    getTableProps,
    getTableBodyProps,
    headerGroups,
    page,
    prepareRow,
  } = useTable(
    {
      columns: COLUMNS,
      data: tableData,
    },
    useSortBy,
    useFilters
  );

  // 显示排序图标辅助函数
  const sortOrder = (isSorted: boolean, isSortedDesc: boolean) => {
    if (!isSorted) return null;
    return isSortedDesc ? '↓' : '↑';
  };

  const PENDING = 'pending'; // 假设该常量已提前定义

  return (
    <table {...getTableProps()} className="table table-striped table-bordered table-hover table-sm">
      <thead>
        {headerGroups.map((headerGroup, thKeyIdx) => (
          <tr key={thKeyIdx} {...headerGroup.getHeaderGroupProps()}>
            {headerGroup.headers.map((column, idx) => (
              <Fragment key={`${thKeyIdx}${idx}`}>
                <th {...column.getHeaderProps()}>
                  <span {...column.getSortByToggleProps()}>
                    {column.render('Header')}
                    <span>{sortOrder(column.isSorted, column.isSortedDesc)}</span>
                  </span>
                  <div>{column.canFilter ? column.render('Filter') : null}</div>
                </th>
                {/* 最后一列添加Action表头 */}
                {idx === headerGroup.headers.length - 1 && <th>Action</th>}
              </Fragment>
            ))}
          </tr>
        ))}
      </thead>
      <tbody {...getTableBodyProps()}>
        {page.length === 0 ? (
          <tr className="no-records">
            <td colSpan={COLUMNS.length + 1}>No records found !!!</td>
          </tr>
        ) : (
          page.map((row, tdKeyIdx) => {
            prepareRow(row);
            return (
              <tr key={tdKeyIdx} {...row.getRowProps()}>
                {row.cells.map((cell, index) => (
                  <Fragment key={`${tdKeyIdx}${index}`}>
                    <td {...cell.getCellProps()}>{cell.render('Cell')}</td>
                    {/* 在最后一列添加操作按钮 */}
                    {index === row.cells.length - 1 && (
                      <td className="view">
                        <button
                          type="button"
                          onClick={() => viewActionModalOpen(row.original)}
                        >
                          {row.original.status === PENDING ? (
                            <i className="fa fa-pencil-square-o" aria-hidden="true" />
                          ) : (
                            <i className="fa fa-eye" aria-hidden="true" />
                          )}
                        </button>
                      </td>
                    )}
                  </Fragment>
                ))}
              </tr>
            );
          })
        )}
      </tbody>
    </table>
  );
};

export default MyTable;

EditModal.tsx(新建Modal组件)

import { useState, useEffect } from 'react';
import { Modal, Button, Form } from 'react-bootstrap'; // 可替换为任意UI库或自定义Modal

interface EditModalProps {
  isOpen: boolean;
  selectedRow: null | {
    tx_id: string;
    incident_id: string;
    resolvedstatus: string;
    resolved_comments: string;
  };
  onClose: () => void;
  onSave: (data: { resolvedstatus: string; resolved_comments: string }) => void;
}

const EditModal = ({ isOpen, selectedRow, onClose, onSave }: EditModalProps) => {
  const [resolvedStatus, setResolvedStatus] = useState('');
  const [resolvedComments, setResolvedComments] = useState('');

  // 打开Modal时初始化输入框值
  useEffect(() => {
    if (selectedRow) {
      setResolvedStatus(selectedRow.resolvedstatus);
      setResolvedComments(selectedRow.resolved_comments);
    }
  }, [selectedRow, isOpen]);

  const handleSave = () => {
    onSave({ resolvedstatus: resolvedStatus, resolved_comments: resolvedComments });
  };

  return (
    <Modal show={isOpen} onHide={onClose}>
      <Modal.Header closeButton>
        <Modal.Title>编辑工单信息</Modal.Title>
      </Modal.Header>
      <Modal.Body>
        <Form>
          <Form.Group className="mb-3">
            <Form.Label>处理状态</Form.Label>
            <Form.Control
              type="text"
              value={resolvedStatus}
              onChange={(e) => setResolvedStatus(e.target.value)}
              placeholder="输入处理状态"
            />
          </Form.Group>
          <Form.Group className="mb-3">
            <Form.Label>处理备注</Form.Label>
            <Form.Control
              as="textarea"
              rows={3}
              value={resolvedComments}
              onChange={(e) => setResolvedComments(e.target.value)}
              placeholder="输入处理备注"
            />
          </Form.Group>
        </Form>
      </Modal.Body>
      <Modal.Footer>
        <Button variant="secondary" onClick={onClose}>
          取消
        </Button>
        <Button variant="primary" onClick={handleSave}>
          保存
        </Button>
      </Modal.Footer>
    </Modal>
  );
};

export default EditModal;

关键说明

  • 状态驱动更新:所有数据变更通过React状态管理,保证表格数据与Modal输入值同步
  • 不可变数据处理:通过map创建新数组更新数据,避免直接修改原数组引发的状态异常
  • 组件解耦设计:Table与Modal通过props传递数据和回调,提升组件复用性

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 15:35:29