如何实现表格多行关联单个Dialog展示数据?
解决方案
核心思路
将Dialog的显示状态、当前要加载的ID统一放在父组件App.tsx中管理,单个Form组件仅负责根据传入的ID请求数据并渲染Dialog内容,避免创建多个组件实例。
修改后的代码
App.tsx
import * as React from 'react'; import { Form } from './Form'; export default function ScrollDialog() { const [dialogOpen, setDialogOpen] = React.useState(false); const [currentTodoId, setCurrentTodoId] = React.useState<number>(0); // 处理表格行按钮点击:设置当前ID并打开Dialog const handleRowClick = (id: number) => { setCurrentTodoId(id); setDialogOpen(true); }; const handleDialogClose = () => { setDialogOpen(false); }; return ( <div> <table border={1}> <thead> <tr> <th>Name</th> <th>Input Data</th> <th>Button</th> </tr> </thead> <tbody> <tr> <td>a</td> <td>1</td> <td> <button onClick={() => handleRowClick(1)}>Get Data</button> </td> </tr> <tr> <td>b</td> <td>2</td> <td> <button onClick={() => handleRowClick(2)}>Get Data</button> </td> </tr> <tr> <td>c</td> <td>3</td> <td> <button onClick={() => handleRowClick(3)}>Get Data</button> </td> </tr> </tbody> </table> {/* 传入Dialog状态、当前ID和关闭回调 */} <Form id={currentTodoId} open={dialogOpen} onClose={handleDialogClose} /> </div> ); }
Form.tsx
import React, { useState, useEffect, useRef } from 'react'; import Button from '@mui/material/Button'; import Dialog, { DialogProps } from '@mui/material/Dialog'; import DialogActions from '@mui/material/DialogActions'; import DialogContent from '@mui/material/DialogContent'; import DialogContentText from '@mui/material/DialogContentText'; import DialogTitle from '@mui/material/DialogTitle'; import useMediaQuery from '@mui/material/useMediaQuery'; import { useTheme } from '@mui/material/styles'; // 定义组件Props类型 type FormProps = { id: number; open: boolean; onClose: () => void; }; export const Form = ({ id, open, onClose }: FormProps) => { const [todoId, setTodoId] = useState<string>(''); const [todoTitle, setTodoTitle] = useState<string>(''); const theme = useTheme(); const fullScreen = useMediaQuery(theme.breakpoints.down('md')); const descriptionElementRef = useRef<HTMLElement>(null); // 当ID变化时,请求对应数据 useEffect(() => { if (id === 0) return; // 初始ID为0时不请求 const fetchTodoData = async () => { try { const response = await fetch(`https://jsonplaceholder.typicode.com/todos/${id}`); const data = await response.json(); setTodoId(data.id.toString()); setTodoTitle(data.title); } catch (error) { console.error('请求数据失败:', error); } }; fetchTodoData(); }, [id]); // Dialog打开时聚焦描述文本 useEffect(() => { if (open && descriptionElementRef.current) { descriptionElementRef.current.focus(); } }, [open]); return ( <Dialog fullScreen={fullScreen} open={open} scroll="body" aria-labelledby="scroll-dialog-title" aria-describedby="scroll-dialog-description" > <DialogTitle id="scroll-dialog-title">Todo 详情</DialogTitle> <DialogContent dividers> <DialogContentText id="scroll-dialog-description" ref={descriptionElementRef} tabIndex={-1} > ID: {todoId} <br /> 标题: {todoTitle} </DialogContentText> </DialogContent> <DialogActions> <Button onClick={onClose}>关闭</Button> </DialogActions> </Dialog> ); };
关键修改说明
- 状态提升:将Dialog的
open状态和当前要加载的currentTodoId移到父组件App.tsx,确保单个Form组件能响应所有行的点击事件。 - 表格按钮逻辑:每行按钮点击时,将该行的
Input Data作为ID传入handleRowClick,设置当前ID并打开Dialog。 - Form组件职责简化:移除Form内部的按钮和独立的
open状态,仅负责接收父组件传递的状态、ID和回调,专注于数据请求和Dialog渲染。 - 动态请求URL:将请求URL中的硬编码
1替换为传入的id,实现根据不同ID加载对应数据。 - 错误处理:添加请求失败的错误捕获,避免程序崩溃。
内容的提问来源于stack exchange,提问作者HelloWorld1
相关产品推荐
相关产品推荐

