React+Antd项目求助:点击用户按钮无法在Modal显示对应信息
问题分析与解决方案
核心问题
- 重复渲染Modal:每个用户列表项都渲染了独立的
Modal组件,且共用同一个isModalOpen状态。点击按钮时所有Modal都会触发显示,受DOM渲染顺序影响,最终只会展示最后一个用户的Modal内容,无法匹配点击目标用户。 - 缺少当前用户状态:没有存储点击的目标用户数据,Modal无法获取对应用户的信息。
- 分页逻辑错误:
nextUser每次加5,但filteredUsers取的是currentPage到currentPage+6的用户,会导致用户重复显示,且最后一页无法正确展示剩余用户。
修复方案
- 将
Modal从列表项中移出,仅在父组件中渲染一个全局Modal。 - 添加
selectedUser状态,存储当前点击的用户信息。 - 修改
openModal方法,点击时传入当前用户并更新状态。 - 修复分页逻辑,确保每页显示6条数据,且最后一页能完整展示剩余用户。
修改后的完整代码
import { Modal } from "antd"; import 'antd/dist/antd.css'; import { useEffect, useState } from "react"; import { userApi } from "../api/userApi"; import { Usuario } from "../interfaces/fetchAllUserResponse"; export const List = () => { const [users, setUsers] = useState<Usuario[]>([]); const [currentPage, setCurrentPage] = useState(0); const [isModalOpen, setIsModalOpen] = useState(false); // 新增状态存储当前选中的用户 const [selectedUser, setSelectedUser] = useState<Usuario | null>(null); useEffect(() => { userApi.get<Usuario[]>('/default/techJobMission') .then(resp => { setUsers(resp.data); }) .catch(console.log); }, []); // 修正分页切片逻辑,每页固定6条数据 const filteredUsers = (): Usuario[] => { const pageSize = 6; return users.slice(currentPage * pageSize, (currentPage + 1) * pageSize); }; // 计算总页数 const totalPages = Math.ceil(users.length / 6); const nextUser = () => { // 限制页码不超过最后一页 if (currentPage < totalPages - 1) { setCurrentPage(currentPage + 1); } }; const prevUser = () => { if (currentPage > 0) { setCurrentPage(currentPage - 1); } }; // 打开Modal时保存当前用户信息 const openModal = (user: Usuario) => { setSelectedUser(user); setIsModalOpen(true); }; const handleOk = () => { setIsModalOpen(false); setSelectedUser(null); // 关闭时清空选中状态 }; const handleCancel = () => { setIsModalOpen(false); setSelectedUser(null); }; const renderItem = ({ _id, firstName, lastName, ticket, present }: Usuario) => { return ( <tr key={_id}> <td> {(present ? <img className="ticket" src="icons/ticket-green.svg" /> : <img className="ticket" src="icons/ticket-red.svg" />)} </td> <td className="user"> <p className="encabezado">{firstName} {lastName}</p> {(present ? <p className="estado">Ha entrado</p> : <p className="estado">No ha entrado</p>)} </td> <td className="id-user"> <p className="encabezado2">ID</p> <p className="estado">{_id}</p> </td> <td className="ticket-td"> <p className="encabezado2">Nº de ticket</p> <p className="estado">{ticket + 1}</p> </td> <td> <button onClick={() => openModal({ _id, firstName, lastName, ticket, present })} className="btn-modal"> <img src="icons/menu-modal.svg" /> </button> </td> </tr> ); }; return ( <> <table className="table"> <tbody> {filteredUsers().map(renderItem)} </tbody> </table> <br /> <button onClick={prevUser} className="paginacion" disabled={currentPage === 0}> <img src="icons/anterior.svg" /> Anterior </button> <button onClick={nextUser} className="paginacion" disabled={currentPage === totalPages - 1}> Siguiente <img src="icons/siguiente.svg" /> </button> {/* 全局唯一的Modal,基于selectedUser渲染内容 */} <Modal open={isModalOpen} onOk={handleOk} onCancel={handleCancel} title="用户详情"> {selectedUser && ( <div> <p>姓名:{selectedUser.firstName} {selectedUser.lastName}</p> <p>ID:{selectedUser._id}</p> <p>票号:{selectedUser.ticket + 1}</p> <p>状态:{selectedUser.present ? 'Ha entrado' : 'No ha entrado'}</p> </div> )} </Modal> </> ); };
关键修改说明
- 全局Modal:避免重复渲染多个Modal,确保每次仅展示一个弹窗。
- selectedUser状态:点击按钮时保存当前用户,Modal根据该状态渲染对应信息,实现点击用户与弹窗内容的匹配。
- 分页逻辑优化:改为按页码计算切片范围,添加页码边界判断,同时给分页按钮添加
disabled属性优化交互体验。 - 状态清空:Modal关闭时清空
selectedUser,避免下次打开弹窗时显示旧数据。
内容的提问来源于stack exchange,提问作者Agustin Marin Posadas
相关产品推荐
相关产品推荐

