如何通过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
相关产品推荐
相关产品推荐

