React Table map渲染子Modal仅显示最后一条数据问题
问题根因
这个问题和key属性无关,核心是实现逻辑存在4个明显错误:
- 你遍历整个
wallets列表给每一条数据都渲染了一个ModalWallets组件,所有Modal共用同一个布尔类型的modalValue开关。点击按钮将modalValue设为true时,所有Modal会同时被唤起,最后渲染的最后一条数据对应的Modal叠在最上层,视觉上就永远显示最后一条数据。你可以打开浏览器开发者工具查看DOM结构,点击按钮时会同时存在多个弹窗节点。 - 点击行内按钮时没有记录当前操作行的唯一标识,程序根本无法区分你要打开哪条数据的弹窗。
- 子组件中初始化余额的
useEffect依赖为空数组,仅在组件首次挂载时执行一次,哪怕后续传入的余额值变化,输入框也不会同步更新。 - 表格行渲染时用数组下标
index作为key,列表数据变动时容易出现渲染错位。
修复方案
- 不要为每一条数据单独渲染弹窗,整个页面只需要保留1个
ModalWallets组件即可。 - 新增状态存储当前要编辑的钱包数据,点击行内操作按钮时,先把当前行的数据存入该状态,再唤起弹窗。
- 修正子组件
useEffect的依赖项,保证传入的余额变化时本地状态能同步更新。 - 表格行的key替换为每条数据唯一的
uid,不要用数组下标。
修正后代码
父组件 Wallets.js 调整部分
function Wallets() { const [wallets, setWallets] = useState([]) const [currentUser, setCurrentUser] = useState(null); // 替换原来的单一modalValue状态,拆分为弹窗开关、当前编辑数据两个状态 const [modalOpen, setModalOpen] = useState(false); const [currentEditWallet, setCurrentEditWallet] = useState(null); // 点击编辑按钮时传入当前行的钱包数据 const handleAddRemove = (walletItem) => { setCurrentEditWallet(walletItem); setModalOpen(true) } const handleClickEditClose = (value) => { setModalOpen(value) // 关闭弹窗时清空当前编辑数据,避免下次打开残留旧数据 if (!value) setCurrentEditWallet(null) } // ... 其余Firestore监听逻辑保持不变 return ( <div className='Wallets'> <div className='wallets__main'> <div className='wallets__table__admin'> <CTable striped hover> {/* 表头部分保持不变 */} <CTableHead> <CTableRow> <CTableHeaderCell scope="col">No.</CTableHeaderCell> <CTableHeaderCell scope="col">Name</CTableHeaderCell> <CTableHeaderCell scope="col">Email</CTableHeaderCell> <CTableHeaderCell scope="col">Role</CTableHeaderCell> <CTableHeaderCell scope="col">Balance</CTableHeaderCell> <CTableHeaderCell scope="col">Action</CTableHeaderCell> </CTableRow> </CTableHead> <CTableBody> {wallets && wallets.map((item, index) => { item.serial = index + 1; {/* key替换为唯一uid,不要用index */} return <CTableRow key={item.uid}> <CTableHeaderCell scope="row">{item.serial}</CTableHeaderCell> <CTableDataCell>{item.data.name}</CTableDataCell> <CTableDataCell>{item.data.email}</CTableDataCell> <CTableDataCell>{item.data.role}</CTableDataCell> <CTableDataCell>${" "}{item.data.balance}</CTableDataCell> <CTableDataCell> <CButton {/* 点击时传入当前行数据 */} onClick={() => handleAddRemove(item)} > Add Amount </CButton> </CTableDataCell> </CTableRow> })} </CTableBody> </CTable> {/* 删除原来遍历wallets渲染Modal的代码,仅在有选中编辑数据时渲染单个弹窗 */} {currentEditWallet && ( <ModalWallets modalValue={modalOpen} handleClickEditClose={handleClickEditClose} Email={currentEditWallet.data.email} Role={currentEditWallet.data.role} Balance={currentEditWallet.data.balance} Name={currentEditWallet.data.name} id={currentEditWallet.uid} /> )} </div> </div> </div> ) }
子组件 ModalWallets.js 调整部分
function ModalWallets({ modalValue, handleClickEditClose, Email, Role, Balance, Name, id }) { const [balanceState, setBalanceState] = useState(0); // ... 其余更新逻辑保持不变 // 给useEffect加上Balance依赖,保证传入余额变化时同步更新本地状态 useEffect(() => { setBalanceState(Balance) }, [Balance]) // ... 其余弹窗渲染逻辑保持不变 }
相关截图
- 父组件(承载数据表格、触发子Modal弹窗的操作按钮)

- 问题现象:点击表格第一行(对应邮箱为walletUser@gmail.com),弹窗却展示第三行数据

内容的提问来源于stack exchange,提问作者Sharanjeet Singh
相关产品推荐
相关产品推荐

