React实现带Props动态组件渲染,PropertyCardList通用化方案咨询
你的通用列表组件设计思路完全正确,完全不需要为每种卡片单独编写列表组件,重复代码不仅冗余,后续迭代修改布局、加载逻辑时还要同步改多份,维护成本极高。你当前遇到的类型报错,本质是没有用泛型把「传入的卡片组件类型」和「properties数组的类型」做关联,TS无法识别两者的对应关系,才会报字段缺失的错误。
修复方案
把PropertyCardList改造成泛型组件,用泛型约束绑定卡片props和数据类型即可:
1. 改写组件类型定义
import React from "react"; import { Grid, GridProps, GridSpacing } from "@material-ui/core"; import { PropertyBase } from "../../@types/Property/Property"; // 泛型T约束为继承PropertyBase的类型,自动适配不同的property结构 interface Props<T extends PropertyBase> { gridSpacing?: GridSpacing; gridItemProps?: GridProps; loading?: boolean; skeletonsCount?: number; properties: T[]; // 卡片组件的props和泛型T绑定,确保类型对应 cardComponent: React.ComponentType<{ property: T; loading: boolean }>; } const PropertyCardList = <T extends PropertyBase>(props: Props<T>) => { if (props.loading) { return ( <Grid container spacing={props.gridSpacing}> {new Array(props.skeletonsCount).fill(null).map((_, index) => ( <Grid item key={index} {...props.gridItemProps} > <props.cardComponent // loading状态下property不会被实际使用,直接断言为T即可 property={{} as T} loading={true} /> </Grid> ))} </Grid> ) } return ( <Grid container spacing={props.gridSpacing}> {props.properties.map((property) => ( <Grid item key={property.id} {...props.gridItemProps} > <props.cardComponent property={property} loading={false} /> </Grid> ))} </Grid> ); } PropertyCardList.defaultProps = { skeletonsCount: 3, } // 这里要加一层类型断言,避免React.memo抹掉泛型信息 export default React.memo(PropertyCardList) as typeof PropertyCardList;
2. 原有调用逻辑无需修改
你现在的组件调用代码可以完全不变,TS会自动根据你传入的cardComponent和properties匹配对应的泛型类型,不会再报字段缺失的错误。
方案说明
你原来的写法里把properties和cardComponent都定义成了独立的联合类型,TS没法判断你传入的SmallPropertyCard对应的就是SmallPropertyInfo数组,只会认为存在「用LargePropertyCard接收SmallPropertyInfo」的可能性,所以会提示缺少字段。用泛型绑定两个属性的类型之后,两者就形成了关联关系,TS可以做准确的类型推断。
这种设计是非常典型的容器组件实现方式,列表只负责通用的网格布局、加载态骨架屏渲染逻辑,卡片渲染完全交给上层自定义,符合单一职责原则,后续新增卡片类型也不需要修改列表组件的代码,复用性和可扩展性都很好。
内容的提问来源于stack exchange,提问作者Zahid Saeed
相关产品推荐
相关产品推荐

