如何为React可复用表格组件配置动态Row类型?
实现支持动态rows类型的可复用TableRowsComponent组件
核心方案是利用TypeScript泛型特性,让组件的rows类型可以根据使用场景动态传入,同时保证headings与rows的类型匹配,避免类型错误。
修改后的完整代码
import { TableCell, TableRow } from "@mui/material"; import TableCellOperations from "./table-cell-operations.componenet"; // 泛化表头类型,让id严格对应行数据的键 export interface HeadingsTypes<RowsType> { id: keyof RowsType; title: string; label: string; } // 定义泛型Props接口 interface Props<RowsType> { rows?: RowsType[]; headings: HeadingsTypes<RowsType>[]; } // 泛型组件,约束行数据必须包含id字段(用于TableRow的key) const TableRowsComponent = <RowsType extends { id: string | number }>({ rows, headings }: Props<RowsType>) => { return ( <> {rows?.map((row) => ( <TableRow key={row.id}> {headings.map((heading) => ( <TableCell key={String(heading.id)} align="center"> {typeof row[heading.id] === "object" && row[heading.id] !== null ? (row[heading.id] as { title?: string })?.title : heading.id === "status" ? (row as { status?: number }).status == 0 ? "منقضی" : "فعال" : row[heading.id]} </TableCell> ))} <TableCellOperations /> </TableRow> ))} </> ); }; export default TableRowsComponent;
关键修改说明
- 泛型参数约束:组件定义时添加
<RowsType extends { id: string | number }>,确保所有传入的行数据都包含id字段(作为TableRow的唯一标识)。 - 表头类型关联:将
HeadingsTypes的id类型改为keyof RowsType,这样表头的id只能是行数据存在的键,避免拼写错误或不匹配的问题。 - Props泛化:让
rows和headings都依赖泛型参数RowsType,保证两者类型严格匹配。 - 特殊逻辑兼容:针对
status字段的特殊处理添加了类型断言,避免当行数据不包含status字段时出现类型报错。
使用示例
原PropertyListItem场景
import { PropertyListItem } from "@slice/properties.slice"; <TableRowsComponent<PropertyListItem> rows={propertyList} headings={[ { id: "name", title: "名称", label: "名称" }, { id: "status", title: "状态", label: "状态" }, // 其他字段... ]} />
其他类型场景(比如用户数据)
// 定义用户类型 interface User { id: string; username: string; email: string; role: string; } <TableRowsComponent<User> rows={userList} headings={[ { id: "username", title: "用户名", label: "用户名" }, { id: "email", title: "邮箱", label: "邮箱" }, { id: "role", title: "角色", label: "角色" }, ]} />
进阶优化:自定义列渲染逻辑
如果需要针对不同类型的列做个性化渲染,可以给HeadingsTypes添加可选的render函数,让调用者灵活控制列内容:
export interface HeadingsTypes<RowsType> { id: keyof RowsType; title: string; label: string; render?: (value: RowsType[keyof RowsType], row: RowsType) => React.ReactNode; } // 组件内部使用render函数 {heading.render ? heading.render(row[heading.id], row) : ( typeof row[heading.id] === "object" && row[heading.id] !== null ? (row[heading.id] as { title?: string })?.title : heading.id === "status" ? (row as { status?: number }).status == 0 ? "منقضی" : "فعال" : row[heading.id] )}
调用时自定义渲染:
<TableRowsComponent<User> rows={userList} headings={[ { id: "username", title: "用户名", label: "用户名" }, { id: "role", title: "角色", label: "角色", render: (role) => <span className={`role-tag role-${role}`}>{role}</span> }, ]} />
内容的提问来源于stack exchange,提问作者Mehrdad Karami Jourabi
相关产品推荐
相关产品推荐

